Parallel placer now works with the custom Slic3r object function. Works an order of magnitude faster.
This commit is contained in:
parent
8617b0a409
commit
e522ad1a00
@ -729,6 +729,7 @@ add_custom_target(pot
|
|||||||
set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d")
|
set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d")
|
||||||
|
|
||||||
add_subdirectory(${LIBDIR}/libnest2d)
|
add_subdirectory(${LIBDIR}/libnest2d)
|
||||||
|
target_compile_definitions(libslic3r PUBLIC -DUSE_TBB)
|
||||||
target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES})
|
target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES})
|
||||||
target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES})
|
target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES})
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ if(LIBNEST2D_UNITTESTS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(LIBNEST2D_BUILD_EXAMPLES)
|
if(LIBNEST2D_BUILD_EXAMPLES)
|
||||||
|
|
||||||
add_executable(example examples/main.cpp
|
add_executable(example examples/main.cpp
|
||||||
# tools/libnfpglue.hpp
|
# tools/libnfpglue.hpp
|
||||||
# tools/libnfpglue.cpp
|
# tools/libnfpglue.cpp
|
||||||
@ -98,8 +99,30 @@ if(LIBNEST2D_BUILD_EXAMPLES)
|
|||||||
tools/svgtools.hpp
|
tools/svgtools.hpp
|
||||||
tests/printer_parts.cpp
|
tests/printer_parts.cpp
|
||||||
tests/printer_parts.h
|
tests/printer_parts.h
|
||||||
${LIBNEST2D_SRCFILES})
|
${LIBNEST2D_SRCFILES}
|
||||||
|
)
|
||||||
|
set(TBB_STATIC ON)
|
||||||
|
find_package(TBB QUIET)
|
||||||
|
if(TBB_FOUND)
|
||||||
|
message(STATUS "Parallelization with Intel TBB")
|
||||||
|
target_include_directories(example PUBLIC ${TBB_INCLUDE_DIRS})
|
||||||
|
target_compile_definitions(example PUBLIC ${TBB_DEFINITIONS} -DUSE_TBB)
|
||||||
|
if(MSVC)
|
||||||
|
# Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
|
||||||
|
target_compile_definitions(example PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE)
|
||||||
|
endif()
|
||||||
|
# The Intel TBB library will use the std::exception_ptr feature of C++11.
|
||||||
|
target_compile_definitions(example PUBLIC -DTBB_USE_CAPTURED_EXCEPTION=1)
|
||||||
|
|
||||||
|
target_link_libraries(example ${TBB_LIBRARIES})
|
||||||
|
else()
|
||||||
|
find_package(OpenMP QUIET)
|
||||||
|
if(OpenMP_CXX_FOUND)
|
||||||
|
message(STATUS "Parallelization with OpenMP")
|
||||||
|
target_include_directories(example PUBLIC OpenMP::OpenMP_CXX)
|
||||||
|
target_link_libraries(example OpenMP::OpenMP_CXX)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(example ${LIBNEST2D_LIBRARIES})
|
target_link_libraries(example ${LIBNEST2D_LIBRARIES})
|
||||||
target_include_directories(example PUBLIC ${LIBNEST2D_HEADERS})
|
target_include_directories(example PUBLIC ${LIBNEST2D_HEADERS})
|
||||||
|
322
xs/src/libnest2d/cmake_modules/FindTBB.cmake
Normal file
322
xs/src/libnest2d/cmake_modules/FindTBB.cmake
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2015 Justus Calvin
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
#
|
||||||
|
# FindTBB
|
||||||
|
# -------
|
||||||
|
#
|
||||||
|
# Find TBB include directories and libraries.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# find_package(TBB [major[.minor]] [EXACT]
|
||||||
|
# [QUIET] [REQUIRED]
|
||||||
|
# [[COMPONENTS] [components...]]
|
||||||
|
# [OPTIONAL_COMPONENTS components...])
|
||||||
|
#
|
||||||
|
# where the allowed components are tbbmalloc and tbb_preview. Users may modify
|
||||||
|
# the behavior of this module with the following variables:
|
||||||
|
#
|
||||||
|
# * TBB_ROOT_DIR - The base directory the of TBB installation.
|
||||||
|
# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files.
|
||||||
|
# * TBB_LIBRARY - The directory that contains the TBB library files.
|
||||||
|
# * TBB_<library>_LIBRARY - The path of the TBB the corresponding TBB library.
|
||||||
|
# These libraries, if specified, override the
|
||||||
|
# corresponding library search results, where <library>
|
||||||
|
# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug,
|
||||||
|
# tbb_preview, or tbb_preview_debug.
|
||||||
|
# * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will
|
||||||
|
# be used instead of the release version.
|
||||||
|
# * TBB_STATIC - Static linking of libraries with a _static suffix.
|
||||||
|
# For example, on Windows a tbb_static.lib will be searched for
|
||||||
|
# instead of tbb.lib.
|
||||||
|
#
|
||||||
|
# Users may modify the behavior of this module with the following environment
|
||||||
|
# variables:
|
||||||
|
#
|
||||||
|
# * TBB_INSTALL_DIR
|
||||||
|
# * TBBROOT
|
||||||
|
# * LIBRARY_PATH
|
||||||
|
#
|
||||||
|
# This module will set the following variables:
|
||||||
|
#
|
||||||
|
# * TBB_FOUND - Set to false, or undefined, if we haven’t found, or
|
||||||
|
# don’t want to use TBB.
|
||||||
|
# * TBB_<component>_FOUND - If False, optional <component> part of TBB sytem is
|
||||||
|
# not available.
|
||||||
|
# * TBB_VERSION - The full version string
|
||||||
|
# * TBB_VERSION_MAJOR - The major version
|
||||||
|
# * TBB_VERSION_MINOR - The minor version
|
||||||
|
# * TBB_INTERFACE_VERSION - The interface version number defined in
|
||||||
|
# tbb/tbb_stddef.h.
|
||||||
|
# * TBB_<library>_LIBRARY_RELEASE - The path of the TBB release version of
|
||||||
|
# <library>, where <library> may be tbb, tbb_debug,
|
||||||
|
# tbbmalloc, tbbmalloc_debug, tbb_preview, or
|
||||||
|
# tbb_preview_debug.
|
||||||
|
# * TBB_<library>_LIBRARY_DEGUG - The path of the TBB release version of
|
||||||
|
# <library>, where <library> may be tbb, tbb_debug,
|
||||||
|
# tbbmalloc, tbbmalloc_debug, tbb_preview, or
|
||||||
|
# tbb_preview_debug.
|
||||||
|
#
|
||||||
|
# The following varibles should be used to build and link with TBB:
|
||||||
|
#
|
||||||
|
# * TBB_INCLUDE_DIRS - The include directory for TBB.
|
||||||
|
# * TBB_LIBRARIES - The libraries to link against to use TBB.
|
||||||
|
# * TBB_LIBRARIES_RELEASE - The release libraries to link against to use TBB.
|
||||||
|
# * TBB_LIBRARIES_DEBUG - The debug libraries to link against to use TBB.
|
||||||
|
# * TBB_DEFINITIONS - Definitions to use when compiling code that uses
|
||||||
|
# TBB.
|
||||||
|
# * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that
|
||||||
|
# uses TBB.
|
||||||
|
# * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that
|
||||||
|
# uses TBB.
|
||||||
|
#
|
||||||
|
# This module will also create the "tbb" target that may be used when building
|
||||||
|
# executables and libraries.
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
if(NOT TBB_FOUND)
|
||||||
|
|
||||||
|
##################################
|
||||||
|
# Check the build type
|
||||||
|
##################################
|
||||||
|
|
||||||
|
if(NOT DEFINED TBB_USE_DEBUG_BUILD)
|
||||||
|
if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)")
|
||||||
|
set(TBB_BUILD_TYPE DEBUG)
|
||||||
|
else()
|
||||||
|
set(TBB_BUILD_TYPE RELEASE)
|
||||||
|
endif()
|
||||||
|
elseif(TBB_USE_DEBUG_BUILD)
|
||||||
|
set(TBB_BUILD_TYPE DEBUG)
|
||||||
|
else()
|
||||||
|
set(TBB_BUILD_TYPE RELEASE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
##################################
|
||||||
|
# Set the TBB search directories
|
||||||
|
##################################
|
||||||
|
|
||||||
|
# Define search paths based on user input and environment variables
|
||||||
|
set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT})
|
||||||
|
|
||||||
|
# Define the search directories based on the current platform
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||||
|
set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB"
|
||||||
|
"C:/Program Files (x86)/Intel/TBB")
|
||||||
|
|
||||||
|
# Set the target architecture
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(TBB_ARCHITECTURE "intel64")
|
||||||
|
else()
|
||||||
|
set(TBB_ARCHITECTURE "ia32")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set the TBB search library path search suffix based on the version of VC
|
||||||
|
if(WINDOWS_STORE)
|
||||||
|
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui")
|
||||||
|
elseif(MSVC14)
|
||||||
|
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14")
|
||||||
|
elseif(MSVC12)
|
||||||
|
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12")
|
||||||
|
elseif(MSVC11)
|
||||||
|
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11")
|
||||||
|
elseif(MSVC10)
|
||||||
|
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add the library path search suffix for the VC independent version of TBB
|
||||||
|
list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt")
|
||||||
|
|
||||||
|
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
|
# OS X
|
||||||
|
set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
|
||||||
|
|
||||||
|
# TODO: Check to see which C++ library is being used by the compiler.
|
||||||
|
if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0)
|
||||||
|
# The default C++ library on OS X 10.9 and later is libc++
|
||||||
|
set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib")
|
||||||
|
else()
|
||||||
|
set(TBB_LIB_PATH_SUFFIX "lib")
|
||||||
|
endif()
|
||||||
|
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
# Linux
|
||||||
|
set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
|
||||||
|
|
||||||
|
# TODO: Check compiler version to see the suffix should be <arch>/gcc4.1 or
|
||||||
|
# <arch>/gcc4.1. For now, assume that the compiler is more recent than
|
||||||
|
# gcc 4.4.x or later.
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
|
||||||
|
set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4")
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
|
||||||
|
set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
##################################
|
||||||
|
# Find the TBB include dir
|
||||||
|
##################################
|
||||||
|
|
||||||
|
find_path(TBB_INCLUDE_DIRS tbb/tbb.h
|
||||||
|
HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR}
|
||||||
|
PATHS ${TBB_DEFAULT_SEARCH_DIR}
|
||||||
|
PATH_SUFFIXES include)
|
||||||
|
|
||||||
|
##################################
|
||||||
|
# Set version strings
|
||||||
|
##################################
|
||||||
|
|
||||||
|
if(TBB_INCLUDE_DIRS)
|
||||||
|
file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file)
|
||||||
|
string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1"
|
||||||
|
TBB_VERSION_MAJOR "${_tbb_version_file}")
|
||||||
|
string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1"
|
||||||
|
TBB_VERSION_MINOR "${_tbb_version_file}")
|
||||||
|
string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1"
|
||||||
|
TBB_INTERFACE_VERSION "${_tbb_version_file}")
|
||||||
|
set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
##################################
|
||||||
|
# Find TBB components
|
||||||
|
##################################
|
||||||
|
|
||||||
|
if(TBB_VERSION VERSION_LESS 4.3)
|
||||||
|
set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb)
|
||||||
|
else()
|
||||||
|
set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TBB_STATIC)
|
||||||
|
set(TBB_STATIC_SUFFIX "_static")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Find each component
|
||||||
|
foreach(_comp ${TBB_SEARCH_COMPOMPONENTS})
|
||||||
|
if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};")
|
||||||
|
|
||||||
|
# Search for the libraries
|
||||||
|
find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX}
|
||||||
|
HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
|
||||||
|
PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
|
||||||
|
PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
|
||||||
|
|
||||||
|
find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug
|
||||||
|
HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
|
||||||
|
PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
|
||||||
|
PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
|
||||||
|
|
||||||
|
if(TBB_${_comp}_LIBRARY_DEBUG)
|
||||||
|
list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}")
|
||||||
|
endif()
|
||||||
|
if(TBB_${_comp}_LIBRARY_RELEASE)
|
||||||
|
list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}")
|
||||||
|
endif()
|
||||||
|
if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY)
|
||||||
|
set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}")
|
||||||
|
set(TBB_${_comp}_FOUND TRUE)
|
||||||
|
else()
|
||||||
|
set(TBB_${_comp}_FOUND FALSE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Mark internal variables as advanced
|
||||||
|
mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE)
|
||||||
|
mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG)
|
||||||
|
mark_as_advanced(TBB_${_comp}_LIBRARY)
|
||||||
|
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
unset(TBB_STATIC_SUFFIX)
|
||||||
|
|
||||||
|
##################################
|
||||||
|
# Set compile flags and libraries
|
||||||
|
##################################
|
||||||
|
|
||||||
|
set(TBB_DEFINITIONS_RELEASE "")
|
||||||
|
set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1")
|
||||||
|
|
||||||
|
if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
|
||||||
|
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${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()
|
||||||
|
|
||||||
|
find_package_handle_standard_args(TBB
|
||||||
|
REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES
|
||||||
|
HANDLE_COMPONENTS
|
||||||
|
VERSION_VAR TBB_VERSION)
|
||||||
|
|
||||||
|
##################################
|
||||||
|
# Create targets
|
||||||
|
##################################
|
||||||
|
|
||||||
|
if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
|
||||||
|
add_library(tbb SHARED IMPORTED)
|
||||||
|
set_target_properties(tbb PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
|
||||||
|
IMPORTED_LOCATION ${TBB_LIBRARIES})
|
||||||
|
if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG)
|
||||||
|
set_target_properties(tbb PROPERTIES
|
||||||
|
INTERFACE_COMPILE_DEFINITIONS "$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:TBB_USE_DEBUG=1>"
|
||||||
|
IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG}
|
||||||
|
IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG}
|
||||||
|
IMPORTED_LOCATION_RELEASE ${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()
|
||||||
|
|
||||||
|
mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES)
|
||||||
|
|
||||||
|
unset(TBB_ARCHITECTURE)
|
||||||
|
unset(TBB_BUILD_TYPE)
|
||||||
|
unset(TBB_LIB_PATH_SUFFIX)
|
||||||
|
unset(TBB_DEFAULT_SEARCH_DIR)
|
||||||
|
|
||||||
|
if(TBB_DEBUG)
|
||||||
|
message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}")
|
||||||
|
message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}")
|
||||||
|
message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}")
|
||||||
|
message(STATUS " TBB_DEFINITIONS_DEBUG = ${TBB_DEFINITIONS_DEBUG}")
|
||||||
|
message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}")
|
||||||
|
message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}")
|
||||||
|
message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endif()
|
@ -54,7 +54,7 @@ void arrangeRectangles() {
|
|||||||
|
|
||||||
const int SCALE = 1000000;
|
const int SCALE = 1000000;
|
||||||
|
|
||||||
std::vector<Item> rects(100, {
|
std::vector<Item> rects(202, {
|
||||||
{-9945219, -3065619},
|
{-9945219, -3065619},
|
||||||
{-9781479, -2031780},
|
{-9781479, -2031780},
|
||||||
{-9510560, -1020730},
|
{-9510560, -1020730},
|
||||||
@ -104,8 +104,6 @@ void arrangeRectangles() {
|
|||||||
// input.insert(input.end(), prusaExParts().begin(), prusaExParts().end());
|
// input.insert(input.end(), prusaExParts().begin(), prusaExParts().end());
|
||||||
// input.insert(input.end(), stegoParts().begin(), stegoParts().end());
|
// input.insert(input.end(), stegoParts().begin(), stegoParts().end());
|
||||||
// input.insert(input.end(), rects.begin(), rects.end());
|
// input.insert(input.end(), rects.begin(), rects.end());
|
||||||
// input.insert(input.end(), proba.begin(), proba.end());
|
|
||||||
// input.insert(input.end(), crasher.begin(), crasher.end());
|
|
||||||
|
|
||||||
Box bin(250*SCALE, 210*SCALE);
|
Box bin(250*SCALE, 210*SCALE);
|
||||||
// PolygonImpl bin = {
|
// PolygonImpl bin = {
|
||||||
@ -123,11 +121,11 @@ void arrangeRectangles() {
|
|||||||
// {}
|
// {}
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// _Circle<PointImpl> bin({0, 0}, 125*SCALE);
|
// Circle bin({0, 0}, 125*SCALE);
|
||||||
|
|
||||||
auto min_obj_distance = static_cast<Coord>(1.5*SCALE);
|
auto min_obj_distance = static_cast<Coord>(6*SCALE);
|
||||||
|
|
||||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
|
using Placer = placers::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
|
||||||
using Packer = Nester<Placer, FirstFitSelection>;
|
using Packer = Nester<Placer, FirstFitSelection>;
|
||||||
|
|
||||||
Packer arrange(bin, min_obj_distance);
|
Packer arrange(bin, min_obj_distance);
|
||||||
@ -136,7 +134,7 @@ void arrangeRectangles() {
|
|||||||
pconf.alignment = Placer::Config::Alignment::CENTER;
|
pconf.alignment = Placer::Config::Alignment::CENTER;
|
||||||
pconf.starting_point = Placer::Config::Alignment::CENTER;
|
pconf.starting_point = Placer::Config::Alignment::CENTER;
|
||||||
pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/};
|
pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/};
|
||||||
pconf.accuracy = 0.5f;
|
pconf.accuracy = 0.65f;
|
||||||
pconf.parallel = true;
|
pconf.parallel = true;
|
||||||
|
|
||||||
Packer::SelectionConfig sconf;
|
Packer::SelectionConfig sconf;
|
||||||
@ -149,12 +147,6 @@ void arrangeRectangles() {
|
|||||||
arrange.configure(pconf, sconf);
|
arrange.configure(pconf, sconf);
|
||||||
|
|
||||||
arrange.progressIndicator([&](unsigned r){
|
arrange.progressIndicator([&](unsigned r){
|
||||||
// svg::SVGWriter::Config conf;
|
|
||||||
// conf.mm_in_coord_units = SCALE;
|
|
||||||
// svg::SVGWriter svgw(conf);
|
|
||||||
// svgw.setSize(bin);
|
|
||||||
// svgw.writePackGroup(arrange.lastResult());
|
|
||||||
// svgw.save("debout");
|
|
||||||
std::cout << "Remaining items: " << r << std::endl;
|
std::cout << "Remaining items: " << r << std::endl;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -201,10 +193,10 @@ void arrangeRectangles() {
|
|||||||
for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); }
|
for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); }
|
||||||
std::cout << ") Total: " << total << std::endl;
|
std::cout << ") Total: " << total << std::endl;
|
||||||
|
|
||||||
for(auto& it : input) {
|
// for(auto& it : input) {
|
||||||
auto ret = sl::isValid(it.transformedShape());
|
// auto ret = sl::isValid(it.transformedShape());
|
||||||
std::cout << ret.second << std::endl;
|
// std::cout << ret.second << std::endl;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if(total != input.size()) std::cout << "ERROR " << "could not pack "
|
if(total != input.size()) std::cout << "ERROR " << "could not pack "
|
||||||
<< input.size() - total << " elements!"
|
<< input.size() - total << " elements!"
|
||||||
@ -222,7 +214,5 @@ void arrangeRectangles() {
|
|||||||
|
|
||||||
int main(void /*int argc, char **argv*/) {
|
int main(void /*int argc, char **argv*/) {
|
||||||
arrangeRectangles();
|
arrangeRectangles();
|
||||||
//// findDegenerateCase();
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -30,12 +30,12 @@ using Rectangle = _Rectangle<PolygonImpl>;
|
|||||||
using PackGroup = _PackGroup<PolygonImpl>;
|
using PackGroup = _PackGroup<PolygonImpl>;
|
||||||
using IndexedPackGroup = _IndexedPackGroup<PolygonImpl>;
|
using IndexedPackGroup = _IndexedPackGroup<PolygonImpl>;
|
||||||
|
|
||||||
using FillerSelection = strategies::_FillerSelection<PolygonImpl>;
|
using FillerSelection = selections::_FillerSelection<PolygonImpl>;
|
||||||
using FirstFitSelection = strategies::_FirstFitSelection<PolygonImpl>;
|
using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>;
|
||||||
using DJDHeuristic = strategies::_DJDHeuristic<PolygonImpl>;
|
using DJDHeuristic = selections::_DJDHeuristic<PolygonImpl>;
|
||||||
|
|
||||||
using NfpPlacer = strategies::_NofitPolyPlacer<PolygonImpl>;
|
using NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl>;
|
||||||
using BottomLeftPlacer = strategies::_BottomLeftPlacer<PolygonImpl>;
|
using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ inline TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
|
|||||||
auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh),
|
auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh),
|
||||||
__nfp::_vsort<RawShape>);
|
__nfp::_vsort<RawShape>);
|
||||||
|
|
||||||
return *it;
|
return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,7 +118,7 @@ TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
|
|||||||
auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh),
|
auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh),
|
||||||
__nfp::_vsort<RawShape>);
|
__nfp::_vsort<RawShape>);
|
||||||
|
|
||||||
return *it;
|
return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,8 +65,8 @@ class _Item {
|
|||||||
mutable VertexConstIterator lmb_; // leftmost bottom vertex
|
mutable VertexConstIterator lmb_; // leftmost bottom vertex
|
||||||
mutable bool rmt_valid_ = false, lmb_valid_ = false;
|
mutable bool rmt_valid_ = false, lmb_valid_ = false;
|
||||||
mutable struct BBCache {
|
mutable struct BBCache {
|
||||||
Box bb; bool valid; Vertex tr;
|
Box bb; bool valid;
|
||||||
BBCache(): valid(false), tr(0, 0) {}
|
BBCache(): valid(false) {}
|
||||||
} bb_cache_;
|
} bb_cache_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -310,7 +310,7 @@ public:
|
|||||||
{
|
{
|
||||||
if(translation_ != tr) {
|
if(translation_ != tr) {
|
||||||
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
|
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
|
||||||
bb_cache_.valid = false;
|
//bb_cache_.valid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,13 +345,19 @@ public:
|
|||||||
|
|
||||||
inline Box boundingBox() const {
|
inline Box boundingBox() const {
|
||||||
if(!bb_cache_.valid) {
|
if(!bb_cache_.valid) {
|
||||||
bb_cache_.bb = sl::boundingBox(transformedShape());
|
if(!has_rotation_)
|
||||||
bb_cache_.tr = {0, 0};
|
bb_cache_.bb = sl::boundingBox(offsettedShape());
|
||||||
|
else {
|
||||||
|
// TODO make sure this works
|
||||||
|
auto rotsh = offsettedShape();
|
||||||
|
sl::rotate(rotsh, rotation_);
|
||||||
|
bb_cache_.bb = sl::boundingBox(rotsh);
|
||||||
|
}
|
||||||
bb_cache_.valid = true;
|
bb_cache_.valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &bb = bb_cache_.bb; auto &tr = bb_cache_.tr;
|
auto &bb = bb_cache_.bb; auto &tr = translation_;
|
||||||
return {bb.minCorner() + tr, bb.maxCorner() + tr};
|
return {bb.minCorner() + tr, bb.maxCorner() + tr };
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Vertex referenceVertex() const {
|
inline Vertex referenceVertex() const {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "placer_boilerplate.hpp"
|
#include "placer_boilerplate.hpp"
|
||||||
|
|
||||||
namespace libnest2d { namespace strategies {
|
namespace libnest2d { namespace placers {
|
||||||
|
|
||||||
template<class T, class = T> struct Epsilon {};
|
template<class T, class = T> struct Epsilon {};
|
||||||
|
|
||||||
|
@ -21,6 +21,12 @@
|
|||||||
|
|
||||||
#include "tools/svgtools.hpp"
|
#include "tools/svgtools.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_TBB
|
||||||
|
#include <tbb/parallel_for.h>
|
||||||
|
#elif defined(_OPENMP)
|
||||||
|
#include <omp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace libnest2d {
|
namespace libnest2d {
|
||||||
|
|
||||||
namespace __parallel {
|
namespace __parallel {
|
||||||
@ -33,20 +39,52 @@ using TIteratorValue = typename iterator_traits<It>::value_type;
|
|||||||
template<class Iterator>
|
template<class Iterator>
|
||||||
inline void enumerate(
|
inline void enumerate(
|
||||||
Iterator from, Iterator to,
|
Iterator from, Iterator to,
|
||||||
function<void(TIteratorValue<Iterator>, unsigned)> fn,
|
function<void(TIteratorValue<Iterator>, size_t)> fn,
|
||||||
std::launch policy = std::launch::deferred | std::launch::async)
|
std::launch policy = std::launch::deferred | std::launch::async)
|
||||||
{
|
{
|
||||||
auto N = to-from;
|
using TN = size_t;
|
||||||
|
auto iN = to-from;
|
||||||
|
TN N = iN < 0? 0 : TN(iN);
|
||||||
|
|
||||||
|
#ifdef USE_TBB
|
||||||
|
if((policy & std::launch::async) == std::launch::async) {
|
||||||
|
tbb::parallel_for<TN>(0, N, [from, fn] (TN n) { fn(*(from + n), n); } );
|
||||||
|
} else {
|
||||||
|
for(TN n = 0; n < N; n++) fn(*(from + n), n);
|
||||||
|
}
|
||||||
|
#elif defined(_OPENMP)
|
||||||
|
if((policy & std::launch::async) == std::launch::async) {
|
||||||
|
#pragma omp parallel for
|
||||||
|
for(TN n = 0; n < N; n++) fn(*(from + n), n);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for(TN n = 0; n < N; n++) fn(*(from + n), n);
|
||||||
|
}
|
||||||
|
#else
|
||||||
std::vector<std::future<void>> rets(N);
|
std::vector<std::future<void>> rets(N);
|
||||||
|
|
||||||
auto it = from;
|
auto it = from;
|
||||||
for(unsigned b = 0; b < N; b++) {
|
for(TN b = 0; b < N; b++) {
|
||||||
rets[b] = std::async(policy, fn, *it++, b);
|
rets[b] = std::async(policy, fn, *it++, unsigned(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(unsigned fi = 0; fi < rets.size(); ++fi) rets[fi].wait();
|
for(TN fi = 0; fi < N; ++fi) rets[fi].wait();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SpinLock {
|
||||||
|
static std::atomic_flag locked;
|
||||||
|
public:
|
||||||
|
void lock() {
|
||||||
|
while (locked.test_and_set(std::memory_order_acquire)) { ; }
|
||||||
|
}
|
||||||
|
void unlock() {
|
||||||
|
locked.clear(std::memory_order_release);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::atomic_flag SpinLock::locked = ATOMIC_FLAG_INIT ;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace __itemhash {
|
namespace __itemhash {
|
||||||
@ -98,7 +136,7 @@ using Hash = std::unordered_map<Key, nfp::NfpResult<S>>;
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace strategies {
|
namespace placers {
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
struct NfpPConfig {
|
struct NfpPConfig {
|
||||||
@ -134,30 +172,12 @@ struct NfpPConfig {
|
|||||||
* that will optimize for the best pack efficiency. With a custom fitting
|
* that will optimize for the best pack efficiency. With a custom fitting
|
||||||
* function you can e.g. influence the shape of the arranged pile.
|
* function you can e.g. influence the shape of the arranged pile.
|
||||||
*
|
*
|
||||||
* \param shapes The first parameter is a container with all the placed
|
* \param item The only parameter is the candidate item which has info
|
||||||
* polygons excluding the current candidate. You can calculate a bounding
|
* about its current position. Your job is to rate this position compared to
|
||||||
* box or convex hull on this pile of polygons without the candidate item
|
* the already packed items.
|
||||||
* or push back the candidate item into the container and then calculate
|
|
||||||
* some features.
|
|
||||||
*
|
|
||||||
* \param item The second parameter is the candidate item.
|
|
||||||
*
|
|
||||||
* \param remaining A container with the remaining items waiting to be
|
|
||||||
* placed. You can use some features about the remaining items to alter to
|
|
||||||
* score of the current placement. If you know that you have to leave place
|
|
||||||
* for other items as well, that might influence your decision about where
|
|
||||||
* the current candidate should be placed. E.g. imagine three big circles
|
|
||||||
* which you want to place into a box: you might place them in a triangle
|
|
||||||
* shape which has the maximum pack density. But if there is a 4th big
|
|
||||||
* circle than you won't be able to pack it. If you knew apriori that
|
|
||||||
* there four circles are to be placed, you would have placed the first 3
|
|
||||||
* into an L shape. This parameter can be used to make these kind of
|
|
||||||
* decisions (for you or a more intelligent AI).
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
std::function<double(const nfp::Shapes<RawShape>&, const _Item<RawShape>&,
|
std::function<double(const _Item<RawShape>&)> object_function;
|
||||||
const ItemGroup&)>
|
|
||||||
object_function;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The quality of search for an optimal placement.
|
* @brief The quality of search for an optimal placement.
|
||||||
@ -180,6 +200,34 @@ struct NfpPConfig {
|
|||||||
*/
|
*/
|
||||||
bool parallel = true;
|
bool parallel = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief before_packing Callback that is called just before a search for
|
||||||
|
* a new item's position is started. You can use this to create various
|
||||||
|
* cache structures and update them between subsequent packings.
|
||||||
|
*
|
||||||
|
* \param merged pile A polygon that is the union of all items in the bin.
|
||||||
|
*
|
||||||
|
* \param pile The items parameter is a container with all the placed
|
||||||
|
* polygons excluding the current candidate. You can for instance check the
|
||||||
|
* alignment with the candidate item or do anything else.
|
||||||
|
*
|
||||||
|
* \param remaining A container with the remaining items waiting to be
|
||||||
|
* placed. You can use some features about the remaining items to alter to
|
||||||
|
* score of the current placement. If you know that you have to leave place
|
||||||
|
* for other items as well, that might influence your decision about where
|
||||||
|
* the current candidate should be placed. E.g. imagine three big circles
|
||||||
|
* which you want to place into a box: you might place them in a triangle
|
||||||
|
* shape which has the maximum pack density. But if there is a 4th big
|
||||||
|
* circle than you won't be able to pack it. If you knew apriori that
|
||||||
|
* there four circles are to be placed, you would have placed the first 3
|
||||||
|
* into an L shape. This parameter can be used to make these kind of
|
||||||
|
* decisions (for you or a more intelligent AI).
|
||||||
|
*/
|
||||||
|
std::function<void(const nfp::Shapes<RawShape>&, // merged pile
|
||||||
|
const ItemGroup&, // packed items
|
||||||
|
const ItemGroup& // remaining items
|
||||||
|
)> before_packing;
|
||||||
|
|
||||||
NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}),
|
NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}),
|
||||||
alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {}
|
alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {}
|
||||||
};
|
};
|
||||||
@ -428,7 +476,7 @@ Circle minimizeCircle(const RawShape& sh) {
|
|||||||
|
|
||||||
|
|
||||||
opt::StopCriteria stopcr;
|
opt::StopCriteria stopcr;
|
||||||
stopcr.max_iterations = 100;
|
stopcr.max_iterations = 30;
|
||||||
stopcr.relative_score_difference = 1e-3;
|
stopcr.relative_score_difference = 1e-3;
|
||||||
opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
|
opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
|
||||||
|
|
||||||
@ -590,35 +638,25 @@ private:
|
|||||||
{
|
{
|
||||||
using namespace nfp;
|
using namespace nfp;
|
||||||
|
|
||||||
Shapes nfps;
|
Shapes nfps(items_.size());
|
||||||
const Item& trsh = itsh.first;
|
const Item& trsh = itsh.first;
|
||||||
// nfps.reserve(polygons.size());
|
|
||||||
|
|
||||||
// unsigned idx = 0;
|
__parallel::enumerate(items_.begin(), items_.end(),
|
||||||
for(Item& sh : items_) {
|
[&nfps, &trsh](const Item& sh, size_t n)
|
||||||
|
{
|
||||||
|
auto& fixedp = sh.transformedShape();
|
||||||
|
auto& orbp = trsh.transformedShape();
|
||||||
|
auto subnfp_r = noFitPolygon<NfpLevel::CONVEX_ONLY>(fixedp, orbp);
|
||||||
|
correctNfpPosition(subnfp_r, sh, trsh);
|
||||||
|
nfps[n] = subnfp_r.first;
|
||||||
|
});
|
||||||
|
|
||||||
// auto ik = item_keys_[idx++] + itsh.second;
|
// for(auto& n : nfps) {
|
||||||
// auto fnd = nfpcache_.find(ik);
|
// auto valid = sl::isValid(n);
|
||||||
|
// if(!valid.first) std::cout << "Warning: " << valid.second << std::endl;
|
||||||
// nfp::NfpResult<RawShape> subnfp_r;
|
|
||||||
// if(fnd == nfpcache_.end()) {
|
|
||||||
|
|
||||||
auto subnfp_r = noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
|
||||||
sh.transformedShape(), trsh.transformedShape());
|
|
||||||
// nfpcache_[ik] = subnfp_r;
|
|
||||||
// } else {
|
|
||||||
// subnfp_r = fnd->second;
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
correctNfpPosition(subnfp_r, sh, trsh);
|
return nfp::merge(nfps);
|
||||||
|
|
||||||
// nfps.emplace_back(subnfp_r.first);
|
|
||||||
nfps = nfp::merge(nfps, subnfp_r.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
// nfps = nfp::merge(nfps);
|
|
||||||
|
|
||||||
return nfps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Level>
|
template<class Level>
|
||||||
@ -777,6 +815,21 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Box boundingBox(const Box& pilebb, const Box& ibb ) {
|
||||||
|
auto& pminc = pilebb.minCorner();
|
||||||
|
auto& pmaxc = pilebb.maxCorner();
|
||||||
|
auto& iminc = ibb.minCorner();
|
||||||
|
auto& imaxc = ibb.maxCorner();
|
||||||
|
Vertex minc, maxc;
|
||||||
|
|
||||||
|
setX(minc, std::min(getX(pminc), getX(iminc)));
|
||||||
|
setY(minc, std::min(getY(pminc), getY(iminc)));
|
||||||
|
|
||||||
|
setX(maxc, std::max(getX(pmaxc), getX(imaxc)));
|
||||||
|
setY(maxc, std::max(getY(pmaxc), getY(imaxc)));
|
||||||
|
return Box(minc, maxc);
|
||||||
|
}
|
||||||
|
|
||||||
using Edges = EdgeCache<RawShape>;
|
using Edges = EdgeCache<RawShape>;
|
||||||
|
|
||||||
template<class Range = ConstItemRange<typename Base::DefaultIter>>
|
template<class Range = ConstItemRange<typename Base::DefaultIter>>
|
||||||
@ -810,6 +863,7 @@ private:
|
|||||||
|
|
||||||
item.translation(initial_tr);
|
item.translation(initial_tr);
|
||||||
item.rotation(initial_rot + rot);
|
item.rotation(initial_rot + rot);
|
||||||
|
item.boundingBox(); // fill the bb cache
|
||||||
|
|
||||||
// place the new item outside of the print bed to make sure
|
// place the new item outside of the print bed to make sure
|
||||||
// it is disjunct from the current merged pile
|
// it is disjunct from the current merged pile
|
||||||
@ -840,21 +894,17 @@ private:
|
|||||||
auto merged_pile = nfp::merge(pile);
|
auto merged_pile = nfp::merge(pile);
|
||||||
auto& bin = bin_;
|
auto& bin = bin_;
|
||||||
double norm = norm_;
|
double norm = norm_;
|
||||||
|
auto pbb = sl::boundingBox(merged_pile);
|
||||||
|
auto binbb = sl::boundingBox(bin);
|
||||||
|
|
||||||
// This is the kernel part of the object function that is
|
// This is the kernel part of the object function that is
|
||||||
// customizable by the library client
|
// customizable by the library client
|
||||||
auto _objfunc = config_.object_function?
|
auto _objfunc = config_.object_function?
|
||||||
config_.object_function :
|
config_.object_function :
|
||||||
[norm, /*pile_area,*/ bin, merged_pile](
|
[norm, bin, binbb, pbb](const Item& item)
|
||||||
const Pile& /*pile*/,
|
|
||||||
const Item& item,
|
|
||||||
const ItemGroup& /*remaining*/)
|
|
||||||
{
|
{
|
||||||
auto ibb = item.boundingBox();
|
auto ibb = item.boundingBox();
|
||||||
auto binbb = sl::boundingBox(bin);
|
auto fullbb = boundingBox(pbb, ibb);
|
||||||
auto mp = merged_pile;
|
|
||||||
mp.emplace_back(item.transformedShape());
|
|
||||||
auto fullbb = sl::boundingBox(mp);
|
|
||||||
|
|
||||||
double score = pl::distance(ibb.center(), binbb.center());
|
double score = pl::distance(ibb.center(), binbb.center());
|
||||||
score /= norm;
|
score /= norm;
|
||||||
@ -868,15 +918,12 @@ private:
|
|||||||
|
|
||||||
// Our object function for placement
|
// Our object function for placement
|
||||||
auto rawobjfunc =
|
auto rawobjfunc =
|
||||||
[item, _objfunc, iv,
|
[_objfunc, iv, startpos] (Vertex v, Item& itm)
|
||||||
startpos, remlist, pile] (Vertex v)
|
|
||||||
{
|
{
|
||||||
auto d = v - iv;
|
auto d = v - iv;
|
||||||
d += startpos;
|
d += startpos;
|
||||||
Item itm = item;
|
|
||||||
itm.translation(d);
|
itm.translation(d);
|
||||||
|
return _objfunc(itm);
|
||||||
return _objfunc(pile, itm, remlist);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto getNfpPoint = [&ecache](const Optimum& opt)
|
auto getNfpPoint = [&ecache](const Optimum& opt)
|
||||||
@ -906,6 +953,9 @@ private:
|
|||||||
std::launch policy = std::launch::deferred;
|
std::launch policy = std::launch::deferred;
|
||||||
if(config_.parallel) policy |= std::launch::async;
|
if(config_.parallel) policy |= std::launch::async;
|
||||||
|
|
||||||
|
if(config_.before_packing)
|
||||||
|
config_.before_packing(merged_pile, items_, remlist);
|
||||||
|
|
||||||
using OptResult = opt::Result<double>;
|
using OptResult = opt::Result<double>;
|
||||||
using OptResults = std::vector<OptResult>;
|
using OptResults = std::vector<OptResult>;
|
||||||
|
|
||||||
@ -914,21 +964,27 @@ private:
|
|||||||
for(unsigned ch = 0; ch < ecache.size(); ch++) {
|
for(unsigned ch = 0; ch < ecache.size(); ch++) {
|
||||||
auto& cache = ecache[ch];
|
auto& cache = ecache[ch];
|
||||||
|
|
||||||
auto contour_ofn = [rawobjfunc, getNfpPoint, ch]
|
|
||||||
(double relpos)
|
|
||||||
{
|
|
||||||
return rawobjfunc(getNfpPoint(Optimum(relpos, ch)));
|
|
||||||
};
|
|
||||||
|
|
||||||
OptResults results(cache.corners().size());
|
OptResults results(cache.corners().size());
|
||||||
|
|
||||||
|
auto& rofn = rawobjfunc;
|
||||||
|
auto& nfpoint = getNfpPoint;
|
||||||
|
|
||||||
__parallel::enumerate(
|
__parallel::enumerate(
|
||||||
cache.corners().begin(),
|
cache.corners().begin(),
|
||||||
cache.corners().end(),
|
cache.corners().end(),
|
||||||
[&contour_ofn, &results]
|
[&results, &item, &rofn, &nfpoint, ch]
|
||||||
(double pos, unsigned n)
|
(double pos, size_t n)
|
||||||
{
|
{
|
||||||
Optimizer solver;
|
Optimizer solver;
|
||||||
|
|
||||||
|
Item itemcpy = item;
|
||||||
|
auto contour_ofn = [&rofn, &nfpoint, ch, &itemcpy]
|
||||||
|
(double relpos)
|
||||||
|
{
|
||||||
|
Optimum op(relpos, ch);
|
||||||
|
return rofn(nfpoint(op), itemcpy);
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
results[n] = solver.optimize_min(contour_ofn,
|
results[n] = solver.optimize_min(contour_ofn,
|
||||||
opt::initvals<double>(pos),
|
opt::initvals<double>(pos),
|
||||||
@ -959,24 +1015,27 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(unsigned hidx = 0; hidx < cache.holeCount(); ++hidx) {
|
for(unsigned hidx = 0; hidx < cache.holeCount(); ++hidx) {
|
||||||
auto hole_ofn =
|
|
||||||
[rawobjfunc, getNfpPoint, ch, hidx]
|
|
||||||
(double pos)
|
|
||||||
{
|
|
||||||
Optimum opt(pos, ch, hidx);
|
|
||||||
return rawobjfunc(getNfpPoint(opt));
|
|
||||||
};
|
|
||||||
|
|
||||||
results.clear();
|
results.clear();
|
||||||
results.resize(cache.corners(hidx).size());
|
results.resize(cache.corners(hidx).size());
|
||||||
|
|
||||||
// TODO : use parallel for
|
// TODO : use parallel for
|
||||||
__parallel::enumerate(cache.corners(hidx).begin(),
|
__parallel::enumerate(cache.corners(hidx).begin(),
|
||||||
cache.corners(hidx).end(),
|
cache.corners(hidx).end(),
|
||||||
[&hole_ofn, &results]
|
[&results, &item, &nfpoint,
|
||||||
(double pos, unsigned n)
|
&rofn, ch, hidx]
|
||||||
|
(double pos, size_t n)
|
||||||
{
|
{
|
||||||
Optimizer solver;
|
Optimizer solver;
|
||||||
|
|
||||||
|
Item itmcpy = item;
|
||||||
|
auto hole_ofn =
|
||||||
|
[&rofn, &nfpoint, ch, hidx, &itmcpy]
|
||||||
|
(double pos)
|
||||||
|
{
|
||||||
|
Optimum opt(pos, ch, hidx);
|
||||||
|
return rofn(nfpoint(opt), itmcpy);
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
results[n] = solver.optimize_min(hole_ofn,
|
results[n] = solver.optimize_min(hole_ofn,
|
||||||
opt::initvals<double>(pos),
|
opt::initvals<double>(pos),
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "../libnest2d.hpp"
|
#include "../libnest2d.hpp"
|
||||||
|
|
||||||
namespace libnest2d { namespace strategies {
|
namespace libnest2d { namespace placers {
|
||||||
|
|
||||||
struct EmptyConfig {};
|
struct EmptyConfig {};
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "selection_boilerplate.hpp"
|
#include "selection_boilerplate.hpp"
|
||||||
|
|
||||||
namespace libnest2d { namespace strategies {
|
namespace libnest2d { namespace selections {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selection heuristic based on [López-Camacho]\
|
* Selection heuristic based on [López-Camacho]\
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "selection_boilerplate.hpp"
|
#include "selection_boilerplate.hpp"
|
||||||
|
|
||||||
namespace libnest2d { namespace strategies {
|
namespace libnest2d { namespace selections {
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
class _FillerSelection: public SelectionBoilerplate<RawShape> {
|
class _FillerSelection: public SelectionBoilerplate<RawShape> {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "../libnest2d.hpp"
|
#include "../libnest2d.hpp"
|
||||||
#include "selection_boilerplate.hpp"
|
#include "selection_boilerplate.hpp"
|
||||||
|
|
||||||
namespace libnest2d { namespace strategies {
|
namespace libnest2d { namespace selections {
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
class _FirstFitSelection: public SelectionBoilerplate<RawShape> {
|
class _FirstFitSelection: public SelectionBoilerplate<RawShape> {
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
|
|
||||||
#include "../libnest2d.hpp"
|
#include "../libnest2d.hpp"
|
||||||
|
|
||||||
namespace libnest2d {
|
namespace libnest2d { namespace selections {
|
||||||
namespace strategies {
|
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
class SelectionBoilerplate {
|
class SelectionBoilerplate {
|
||||||
|
@ -102,17 +102,17 @@ TEST(BasicFunctionality, creationAndDestruction)
|
|||||||
|
|
||||||
TEST(GeometryAlgorithms, boundingCircle) {
|
TEST(GeometryAlgorithms, boundingCircle) {
|
||||||
using namespace libnest2d;
|
using namespace libnest2d;
|
||||||
using strategies::boundingCircle;
|
using placers::boundingCircle;
|
||||||
|
|
||||||
PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}};
|
PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}};
|
||||||
_Circle<PointImpl> c = boundingCircle<PolygonImpl>(p);
|
Circle c = boundingCircle(p);
|
||||||
|
|
||||||
ASSERT_EQ(c.center().X, 0);
|
ASSERT_EQ(c.center().X, 0);
|
||||||
ASSERT_EQ(c.center().Y, 0);
|
ASSERT_EQ(c.center().Y, 0);
|
||||||
ASSERT_DOUBLE_EQ(c.radius(), 10);
|
ASSERT_DOUBLE_EQ(c.radius(), 10);
|
||||||
|
|
||||||
shapelike::translate(p, PointImpl{10, 10});
|
shapelike::translate(p, PointImpl{10, 10});
|
||||||
c = boundingCircle<PolygonImpl>(p);
|
c = boundingCircle(p);
|
||||||
|
|
||||||
ASSERT_EQ(c.center().X, 10);
|
ASSERT_EQ(c.center().X, 10);
|
||||||
ASSERT_EQ(c.center().Y, 10);
|
ASSERT_EQ(c.center().Y, 10);
|
||||||
@ -712,7 +712,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
|||||||
|
|
||||||
auto& exportfun = exportSVG<SCALE, Box>;
|
auto& exportfun = exportSVG<SCALE, Box>;
|
||||||
|
|
||||||
auto onetest = [&](Item& orbiter, Item& stationary){
|
auto onetest = [&](Item& orbiter, Item& stationary, unsigned testidx){
|
||||||
testcase++;
|
testcase++;
|
||||||
|
|
||||||
orbiter.translate({210*SCALE, 0});
|
orbiter.translate({210*SCALE, 0});
|
||||||
@ -720,15 +720,19 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
|||||||
auto&& nfp = nfp::noFitPolygon<lvl>(stationary.rawShape(),
|
auto&& nfp = nfp::noFitPolygon<lvl>(stationary.rawShape(),
|
||||||
orbiter.transformedShape());
|
orbiter.transformedShape());
|
||||||
|
|
||||||
strategies::correctNfpPosition(nfp, stationary, orbiter);
|
placers::correctNfpPosition(nfp, stationary, orbiter);
|
||||||
|
|
||||||
auto v = shapelike::isValid(nfp.first);
|
auto valid = shapelike::isValid(nfp.first);
|
||||||
|
|
||||||
if(!v.first) {
|
/*Item infp(nfp.first);
|
||||||
std::cout << v.second << std::endl;
|
if(!valid.first) {
|
||||||
}
|
std::cout << "test instance: " << testidx << " "
|
||||||
|
<< valid.second << std::endl;
|
||||||
|
std::vector<std::reference_wrapper<Item>> inp = {std::ref(infp)};
|
||||||
|
exportfun(inp, bin, testidx);
|
||||||
|
}*/
|
||||||
|
|
||||||
ASSERT_TRUE(v.first);
|
ASSERT_TRUE(valid.first);
|
||||||
|
|
||||||
Item infp(nfp.first);
|
Item infp(nfp.first);
|
||||||
|
|
||||||
@ -748,7 +752,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
|||||||
|
|
||||||
bool touching = Item::touches(tmp, stationary);
|
bool touching = Item::touches(tmp, stationary);
|
||||||
|
|
||||||
if(!touching) {
|
if(!touching || !valid.first) {
|
||||||
std::vector<std::reference_wrapper<Item>> inp = {
|
std::vector<std::reference_wrapper<Item>> inp = {
|
||||||
std::ref(stationary), std::ref(tmp), std::ref(infp)
|
std::ref(stationary), std::ref(tmp), std::ref(infp)
|
||||||
};
|
};
|
||||||
@ -760,16 +764,18 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
unsigned tidx = 0;
|
||||||
for(auto& td : testdata) {
|
for(auto& td : testdata) {
|
||||||
auto orbiter = td.orbiter;
|
auto orbiter = td.orbiter;
|
||||||
auto stationary = td.stationary;
|
auto stationary = td.stationary;
|
||||||
onetest(orbiter, stationary);
|
onetest(orbiter, stationary, tidx++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tidx = 0;
|
||||||
for(auto& td : testdata) {
|
for(auto& td : testdata) {
|
||||||
auto orbiter = td.stationary;
|
auto orbiter = td.stationary;
|
||||||
auto stationary = td.orbiter;
|
auto stationary = td.orbiter;
|
||||||
onetest(orbiter, stationary);
|
onetest(orbiter, stationary, tidx++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -796,7 +802,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) {
|
|||||||
|
|
||||||
Rectangle input(10, 10);
|
Rectangle input(10, 10);
|
||||||
|
|
||||||
strategies::EdgeCache<PolygonImpl> ecache(input);
|
placers::EdgeCache<PolygonImpl> ecache(input);
|
||||||
|
|
||||||
auto first = *input.begin();
|
auto first = *input.begin();
|
||||||
ASSERT_TRUE(getX(first) == getX(ecache.coords(0)));
|
ASSERT_TRUE(getX(first) == getX(ecache.coords(0)));
|
||||||
|
@ -32,15 +32,18 @@ template<class S> struct _alg {
|
|||||||
#define dNAN std::nan("")
|
#define dNAN std::nan("")
|
||||||
|
|
||||||
struct Vector {
|
struct Vector {
|
||||||
Coord x, y;
|
Coord x = 0.0, y = 0.0;
|
||||||
bool marked = false;
|
bool marked = false;
|
||||||
Vector() = default;
|
Vector() = default;
|
||||||
Vector(Coord X, Coord Y): x(X), y(Y) {}
|
Vector(Coord X, Coord Y): x(X), y(Y) {}
|
||||||
Vector(const Point& p): x(getX(p)), y(getY(p)) {}
|
Vector(const Point& p): x(Coord(getX(p))), y(Coord(getY(p))) {}
|
||||||
operator Point() const { return {iCoord(x), iCoord(y)}; }
|
operator Point() const { return {iCoord(x), iCoord(y)}; }
|
||||||
Vector& operator=(const Point& p) {
|
Vector& operator=(const Point& p) {
|
||||||
x = getX(p), y = getY(p); return *this;
|
x = getX(p), y = getY(p); return *this;
|
||||||
}
|
}
|
||||||
|
bool operator!=(const Vector& v) const {
|
||||||
|
return v.x != x || v.y != y;
|
||||||
|
}
|
||||||
Vector(std::initializer_list<Coord> il):
|
Vector(std::initializer_list<Coord> il):
|
||||||
x(*il.begin()), y(*std::next(il.begin())) {}
|
x(*il.begin()), y(*std::next(il.begin())) {}
|
||||||
};
|
};
|
||||||
@ -58,8 +61,10 @@ template<class S> struct _alg {
|
|||||||
v_.reserve(c.size());
|
v_.reserve(c.size());
|
||||||
std::transform(c.begin(), c.end(), std::back_inserter(v_),
|
std::transform(c.begin(), c.end(), std::back_inserter(v_),
|
||||||
[](const Point& p) {
|
[](const Point& p) {
|
||||||
return Vector(double(x(p))/1e6, double(y(p))/1e6);
|
return Vector(double(x(p)) / 1e6, double(y(p)) / 1e6);
|
||||||
});
|
});
|
||||||
|
std::reverse(v_.begin(), v_.end());
|
||||||
|
v_.pop_back();
|
||||||
}
|
}
|
||||||
Cntr() = default;
|
Cntr() = default;
|
||||||
|
|
||||||
@ -67,8 +72,10 @@ template<class S> struct _alg {
|
|||||||
Coord offsety = 0;
|
Coord offsety = 0;
|
||||||
size_t size() const { return v_.size(); }
|
size_t size() const { return v_.size(); }
|
||||||
bool empty() const { return v_.empty(); }
|
bool empty() const { return v_.empty(); }
|
||||||
typename Contour::const_iterator begin() const { return v_.cbegin(); }
|
typename std::vector<Vector>::const_iterator cbegin() const { return v_.cbegin(); }
|
||||||
typename Contour::const_iterator end() const { return v_.cend(); }
|
typename std::vector<Vector>::const_iterator cend() const { return v_.cend(); }
|
||||||
|
typename std::vector<Vector>::iterator begin() { return v_.begin(); }
|
||||||
|
typename std::vector<Vector>::iterator end() { return v_.end(); }
|
||||||
Vector& operator[](size_t idx) { return v_[idx]; }
|
Vector& operator[](size_t idx) { return v_[idx]; }
|
||||||
const Vector& operator[](size_t idx) const { return v_[idx]; }
|
const Vector& operator[](size_t idx) const { return v_[idx]; }
|
||||||
template<class...Args>
|
template<class...Args>
|
||||||
@ -83,12 +90,16 @@ template<class S> struct _alg {
|
|||||||
|
|
||||||
operator Contour() const {
|
operator Contour() const {
|
||||||
Contour cnt;
|
Contour cnt;
|
||||||
cnt.reserve(v_.size());
|
cnt.reserve(v_.size() + 1);
|
||||||
std::transform(v_.begin(), v_.end(), std::back_inserter(cnt),
|
std::transform(v_.begin(), v_.end(), std::back_inserter(cnt),
|
||||||
[](const Vector& vertex) {
|
[](const Vector& vertex) {
|
||||||
return Point(iCoord(vertex.x*1e6), iCoord(vertex.y*1e6));
|
return Point(iCoord(vertex.x) * 1000000, iCoord(vertex.y) * 1000000);
|
||||||
});
|
});
|
||||||
return cnt;
|
if(!cnt.empty()) cnt.emplace_back(cnt.front());
|
||||||
|
S sh = shapelike::create<S>(cnt);
|
||||||
|
|
||||||
|
// std::reverse(cnt.begin(), cnt.end());
|
||||||
|
return shapelike::getContour(sh);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -235,11 +246,11 @@ template<class S> struct _alg {
|
|||||||
if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) &&
|
if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) &&
|
||||||
(pdotnorm>s1dotnorm && pdotnorm>s2dotnorm))
|
(pdotnorm>s1dotnorm && pdotnorm>s2dotnorm))
|
||||||
{
|
{
|
||||||
return double(min(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm));
|
return min(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm);
|
||||||
}
|
}
|
||||||
if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) &&
|
if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) &&
|
||||||
(pdotnorm<s1dotnorm && pdotnorm<s2dotnorm)){
|
(pdotnorm<s1dotnorm && pdotnorm<s2dotnorm)){
|
||||||
return double(-min(s1dotnorm-pdotnorm, s2dotnorm-pdotnorm));
|
return -min(s1dotnorm-pdotnorm, s2dotnorm-pdotnorm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,7 +297,7 @@ template<class S> struct _alg {
|
|||||||
auto EFmin = min(dotE, dotF);
|
auto EFmin = min(dotE, dotF);
|
||||||
|
|
||||||
// segments that will merely touch at one point
|
// segments that will merely touch at one point
|
||||||
if(_almostEqual(ABmax, EFmin,TOL) || _almostEqual(ABmin, EFmax,TOL)){
|
if(_almostEqual(ABmax, EFmin, TOL) || _almostEqual(ABmin, EFmax,TOL)) {
|
||||||
return dNAN;
|
return dNAN;
|
||||||
}
|
}
|
||||||
// segments miss eachother completely
|
// segments miss eachother completely
|
||||||
@ -362,7 +373,7 @@ template<class S> struct _alg {
|
|||||||
d = dNAN;
|
d = dNAN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(isnan(d)){
|
if(!isnan(d)){
|
||||||
distances.emplace_back(d);
|
distances.emplace_back(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -392,7 +403,7 @@ template<class S> struct _alg {
|
|||||||
auto d = pointDistance(E,A,B,direction);
|
auto d = pointDistance(E,A,B,direction);
|
||||||
if(!isnan(d) && _almostEqual(d, 0))
|
if(!isnan(d) && _almostEqual(d, 0))
|
||||||
{ // crossF<crossE A currently touches EF, but AB is moving away from EF
|
{ // crossF<crossE A currently touches EF, but AB is moving away from EF
|
||||||
auto dF = pointDistance(F,A,B,direction, true);
|
double dF = pointDistance(F,A,B,direction, true);
|
||||||
if(dF < 0 || _almostEqual(dF*overlap,0)){
|
if(dF < 0 || _almostEqual(dF*overlap,0)){
|
||||||
d = dNAN;
|
d = dNAN;
|
||||||
}
|
}
|
||||||
@ -407,7 +418,7 @@ template<class S> struct _alg {
|
|||||||
if(!isnan(d) && _almostEqual(d, 0))
|
if(!isnan(d) && _almostEqual(d, 0))
|
||||||
{ // && crossE<crossF A currently touches EF,
|
{ // && crossE<crossF A currently touches EF,
|
||||||
// but AB is moving away from EF
|
// but AB is moving away from EF
|
||||||
auto dE = pointDistance(E,A,B,direction, true);
|
double dE = pointDistance(E,A,B,direction, true);
|
||||||
if(dE < 0 || _almostEqual(dE*overlap,0)){
|
if(dE < 0 || _almostEqual(dE*overlap,0)){
|
||||||
d = dNAN;
|
d = dNAN;
|
||||||
}
|
}
|
||||||
@ -424,17 +435,29 @@ template<class S> struct _alg {
|
|||||||
return *std::min_element(distances.begin(), distances.end());
|
return *std::min_element(distances.begin(), distances.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
static double polygonSlideDistance( const Cntr& A,
|
static double polygonSlideDistance( const Cntr& AA,
|
||||||
const Cntr& B,
|
const Cntr& BB,
|
||||||
Vector direction,
|
Vector direction,
|
||||||
bool ignoreNegative)
|
bool ignoreNegative)
|
||||||
{
|
{
|
||||||
// Vector A1, A2, B1, B2;
|
// Vector A1, A2, B1, B2;
|
||||||
|
Cntr A = AA;
|
||||||
|
Cntr B = BB;
|
||||||
|
|
||||||
Coord Aoffsetx = A.offsetx;
|
Coord Aoffsetx = A.offsetx;
|
||||||
Coord Boffsetx = B.offsetx;
|
Coord Boffsetx = B.offsetx;
|
||||||
Coord Aoffsety = A.offsety;
|
Coord Aoffsety = A.offsety;
|
||||||
Coord Boffsety = B.offsety;
|
Coord Boffsety = B.offsety;
|
||||||
|
|
||||||
|
// close the loop for polygons
|
||||||
|
if(A[0] != A[A.size()-1]){
|
||||||
|
A.emplace_back(AA[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(B[0] != B[B.size()-1]){
|
||||||
|
B.emplace_back(BB[0]);
|
||||||
|
}
|
||||||
|
|
||||||
auto& edgeA = A;
|
auto& edgeA = A;
|
||||||
auto& edgeB = B;
|
auto& edgeB = B;
|
||||||
|
|
||||||
@ -457,7 +480,7 @@ template<class S> struct _alg {
|
|||||||
Vector A1 = {x(edgeA[j]) + Aoffsetx, y(edgeA[j]) + Aoffsety };
|
Vector A1 = {x(edgeA[j]) + Aoffsetx, y(edgeA[j]) + Aoffsety };
|
||||||
Vector A2 = {x(edgeA[j+1]) + Aoffsetx, y(edgeA[j+1]) + Aoffsety};
|
Vector A2 = {x(edgeA[j+1]) + Aoffsetx, y(edgeA[j+1]) + Aoffsety};
|
||||||
Vector B1 = {x(edgeB[i]) + Boffsetx, y(edgeB[i]) + Boffsety };
|
Vector B1 = {x(edgeB[i]) + Boffsetx, y(edgeB[i]) + Boffsety };
|
||||||
Vector B2 = {x(edgeB[i+1]) + Boffsety, y(edgeB[i+1]) + Boffsety};
|
Vector B2 = {x(edgeB[i+1]) + Boffsetx, y(edgeB[i+1]) + Boffsety};
|
||||||
|
|
||||||
if((_almostEqual(A1.x, A2.x) && _almostEqual(A1.y, A2.y)) ||
|
if((_almostEqual(A1.x, A2.x) && _almostEqual(A1.y, A2.y)) ||
|
||||||
(_almostEqual(B1.x, B2.x) && _almostEqual(B1.y, B2.y))){
|
(_almostEqual(B1.x, B2.x) && _almostEqual(B1.y, B2.y))){
|
||||||
@ -476,23 +499,26 @@ template<class S> struct _alg {
|
|||||||
return distance;
|
return distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double polygonProjectionDistance(const Cntr& A,
|
static double polygonProjectionDistance(const Cntr& AA,
|
||||||
const Cntr& B,
|
const Cntr& BB,
|
||||||
Vector direction)
|
Vector direction)
|
||||||
{
|
{
|
||||||
|
Cntr A = AA;
|
||||||
|
Cntr B = BB;
|
||||||
|
|
||||||
auto Boffsetx = B.offsetx;
|
auto Boffsetx = B.offsetx;
|
||||||
auto Boffsety = B.offsety;
|
auto Boffsety = B.offsety;
|
||||||
auto Aoffsetx = A.offsetx;
|
auto Aoffsetx = A.offsetx;
|
||||||
auto Aoffsety = A.offsety;
|
auto Aoffsety = A.offsety;
|
||||||
|
|
||||||
// close the loop for polygons
|
// close the loop for polygons
|
||||||
/*if(A[0] != A[A.length-1]){
|
if(A[0] != A[A.size()-1]){
|
||||||
A.push(A[0]);
|
A.push(A[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(B[0] != B[B.length-1]){
|
if(B[0] != B[B.size()-1]){
|
||||||
B.push(B[0]);
|
B.push(B[0]);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
auto& edgeA = A;
|
auto& edgeA = A;
|
||||||
auto& edgeB = B;
|
auto& edgeB = B;
|
||||||
@ -665,7 +691,7 @@ template<class S> struct _alg {
|
|||||||
A.offsetx = 0;
|
A.offsetx = 0;
|
||||||
A.offsety = 0;
|
A.offsety = 0;
|
||||||
|
|
||||||
unsigned i = 0, j = 0;
|
long i = 0, j = 0;
|
||||||
|
|
||||||
auto minA = y(A[0]);
|
auto minA = y(A[0]);
|
||||||
long minAindex = 0;
|
long minAindex = 0;
|
||||||
@ -709,7 +735,8 @@ template<class S> struct _alg {
|
|||||||
|
|
||||||
struct Touch {
|
struct Touch {
|
||||||
int type;
|
int type;
|
||||||
long A, B;
|
long A;
|
||||||
|
long B;
|
||||||
Touch(int t, long a, long b): type(t), A(a), B(b) {}
|
Touch(int t, long a, long b): type(t), A(a), B(b) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -721,6 +748,15 @@ template<class S> struct _alg {
|
|||||||
// maintain a list of touching points/edges
|
// maintain a list of touching points/edges
|
||||||
std::vector<Touch> touching;
|
std::vector<Touch> touching;
|
||||||
|
|
||||||
|
struct V {
|
||||||
|
Coord x, y;
|
||||||
|
Vector *start, *end;
|
||||||
|
operator bool() {
|
||||||
|
return start != nullptr && end != nullptr;
|
||||||
|
}
|
||||||
|
operator Vector() const { return {x, y}; }
|
||||||
|
} prevvector = {0, 0, nullptr, nullptr};
|
||||||
|
|
||||||
Cntr NFP;
|
Cntr NFP;
|
||||||
NFP.emplace_back(x(B[0]) + B.offsetx, y(B[0]) + B.offsety);
|
NFP.emplace_back(x(B[0]) + B.offsetx, y(B[0]) + B.offsety);
|
||||||
|
|
||||||
@ -736,10 +772,10 @@ template<class S> struct _alg {
|
|||||||
|
|
||||||
// find touching vertices/edges
|
// find touching vertices/edges
|
||||||
for(i = 0; i < A.size(); i++){
|
for(i = 0; i < A.size(); i++){
|
||||||
auto nexti = (i == A.size() - 1) ? 0 : i + 1;
|
long nexti = (i == A.size() - 1) ? 0 : i + 1;
|
||||||
for(j = 0; j < B.size(); j++){
|
for(j = 0; j < B.size(); j++){
|
||||||
|
|
||||||
auto nextj = (j == B.size() - 1) ? 0 : j + 1;
|
long nextj = (j == B.size() - 1) ? 0 : j + 1;
|
||||||
|
|
||||||
if( _almostEqual(A[i].x, B[j].x+B.offsetx) &&
|
if( _almostEqual(A[i].x, B[j].x+B.offsetx) &&
|
||||||
_almostEqual(A[i].y, B[j].y+B.offsety))
|
_almostEqual(A[i].y, B[j].y+B.offsety))
|
||||||
@ -762,15 +798,6 @@ template<class S> struct _alg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct V {
|
|
||||||
Coord x, y;
|
|
||||||
Vector *start, *end;
|
|
||||||
operator bool() {
|
|
||||||
return start != nullptr && end != nullptr;
|
|
||||||
}
|
|
||||||
operator Vector() const { return {x, y}; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// generate translation vectors from touching vertices/edges
|
// generate translation vectors from touching vertices/edges
|
||||||
std::vector<V> vectors;
|
std::vector<V> vectors;
|
||||||
for(i=0; i < touching.size(); i++){
|
for(i=0; i < touching.size(); i++){
|
||||||
@ -871,7 +898,6 @@ template<class S> struct _alg {
|
|||||||
// will cause immediate intersection. For now just check them all
|
// will cause immediate intersection. For now just check them all
|
||||||
|
|
||||||
V translate = {0, 0, nullptr, nullptr};
|
V translate = {0, 0, nullptr, nullptr};
|
||||||
V prevvector = {0, 0, nullptr, nullptr};
|
|
||||||
double maxd = 0;
|
double maxd = 0;
|
||||||
|
|
||||||
for(i = 0; i < vectors.size(); i++) {
|
for(i = 0; i < vectors.size(); i++) {
|
||||||
@ -897,7 +923,8 @@ template<class S> struct _alg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double d = polygonSlideDistance(A, B, vectors[i], true);
|
V vi = vectors[i];
|
||||||
|
double d = polygonSlideDistance(A, B, vi, true);
|
||||||
double vecd2 = vectors[i].x*vectors[i].x + vectors[i].y*vectors[i].y;
|
double vecd2 = vectors[i].x*vectors[i].x + vectors[i].y*vectors[i].y;
|
||||||
|
|
||||||
if(isnan(d) || d*d > vecd2){
|
if(isnan(d) || d*d > vecd2){
|
||||||
@ -985,19 +1012,6 @@ template<class S> struct _alg {
|
|||||||
|
|
||||||
template<class S> const double _alg<S>::TOL = std::pow(10, -9);
|
template<class S> const double _alg<S>::TOL = std::pow(10, -9);
|
||||||
|
|
||||||
//template<class S>
|
|
||||||
//nfp::NfpResult<S> nfpSimpleSimple(const S& stat, const S& orb) {
|
|
||||||
//// using Cntr = TContour<S>;
|
|
||||||
// using Point = TPoint<S>;
|
|
||||||
//// using Coord = TCoord<Point>;
|
|
||||||
//// using Shapes = nfp::Shapes<S>;
|
|
||||||
|
|
||||||
// namespace sl = shapelike;
|
|
||||||
|
|
||||||
// noFitPolygon(sl::getContour(stat), sl::getContour(orb), true, true);
|
|
||||||
// return {S(), Point()};
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,13 +25,11 @@ template<> struct NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
|
|||||||
namespace sl = shapelike;
|
namespace sl = shapelike;
|
||||||
using alg = __svgnest::_alg<PolygonImpl>;
|
using alg = __svgnest::_alg<PolygonImpl>;
|
||||||
|
|
||||||
std::cout << "Itt vagyok" << std::endl;
|
|
||||||
auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
|
auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
|
||||||
sl::getContour(cother), false, false);
|
sl::getContour(cother), false, false);
|
||||||
|
|
||||||
PolygonImpl nfp_cntr;
|
PolygonImpl nfp_cntr;
|
||||||
nfp_cntr.Contour = nfp_p.front();
|
if(!nfp_p.empty()) nfp_cntr.Contour = nfp_p.front();
|
||||||
std::cout << "Contour size: " << nfp_cntr.Contour.size() << std::endl;
|
|
||||||
return {nfp_cntr, referenceVertex(nfp_cntr)};
|
return {nfp_cntr, referenceVertex(nfp_cntr)};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -100,55 +100,54 @@ namespace bgi = boost::geometry::index;
|
|||||||
using SpatElement = std::pair<Box, unsigned>;
|
using SpatElement = std::pair<Box, unsigned>;
|
||||||
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
|
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
|
||||||
using ItemGroup = std::vector<std::reference_wrapper<Item>>;
|
using ItemGroup = std::vector<std::reference_wrapper<Item>>;
|
||||||
|
template<class TBin>
|
||||||
|
using TPacker = typename placers::_NofitPolyPlacer<PolygonImpl, TBin>;
|
||||||
|
|
||||||
|
const double BIG_ITEM_TRESHOLD = 0.02;
|
||||||
|
|
||||||
|
Box boundingBox(const Box& pilebb, const Box& ibb ) {
|
||||||
|
auto& pminc = pilebb.minCorner();
|
||||||
|
auto& pmaxc = pilebb.maxCorner();
|
||||||
|
auto& iminc = ibb.minCorner();
|
||||||
|
auto& imaxc = ibb.maxCorner();
|
||||||
|
PointImpl minc, maxc;
|
||||||
|
|
||||||
|
setX(minc, std::min(getX(pminc), getX(iminc)));
|
||||||
|
setY(minc, std::min(getY(pminc), getY(iminc)));
|
||||||
|
|
||||||
|
setX(maxc, std::max(getX(pmaxc), getX(imaxc)));
|
||||||
|
setY(maxc, std::max(getY(pmaxc), getY(imaxc)));
|
||||||
|
return Box(minc, maxc);
|
||||||
|
}
|
||||||
|
|
||||||
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
|
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
|
||||||
objfunc(const PointImpl& bincenter,
|
objfunc(const PointImpl& bincenter,
|
||||||
double bin_area,
|
const shapelike::Shapes<PolygonImpl>& merged_pile,
|
||||||
sl::Shapes<PolygonImpl>& pile, // The currently arranged pile
|
const Box& pilebb,
|
||||||
|
const ItemGroup& items,
|
||||||
const Item &item,
|
const Item &item,
|
||||||
|
double bin_area,
|
||||||
double norm, // A norming factor for physical dimensions
|
double norm, // A norming factor for physical dimensions
|
||||||
std::vector<double>& areacache, // pile item areas will be cached
|
|
||||||
// a spatial index to quickly get neighbors of the candidate item
|
// a spatial index to quickly get neighbors of the candidate item
|
||||||
SpatIndex& spatindex,
|
const SpatIndex& spatindex,
|
||||||
const ItemGroup& remaining
|
const ItemGroup& remaining
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using Coord = TCoord<PointImpl>;
|
using Coord = TCoord<PointImpl>;
|
||||||
|
|
||||||
static const double BIG_ITEM_TRESHOLD = 0.02;
|
|
||||||
static const double ROUNDNESS_RATIO = 0.5;
|
static const double ROUNDNESS_RATIO = 0.5;
|
||||||
static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO;
|
static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO;
|
||||||
|
|
||||||
// We will treat big items (compared to the print bed) differently
|
// We will treat big items (compared to the print bed) differently
|
||||||
auto isBig = [&areacache, bin_area](double a) {
|
auto isBig = [bin_area](double a) {
|
||||||
return a/bin_area > BIG_ITEM_TRESHOLD ;
|
return a/bin_area > BIG_ITEM_TRESHOLD ;
|
||||||
};
|
};
|
||||||
|
|
||||||
// If a new bin has been created:
|
|
||||||
if(pile.size() < areacache.size()) {
|
|
||||||
areacache.clear();
|
|
||||||
spatindex.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// We must fill the caches:
|
|
||||||
int idx = 0;
|
|
||||||
for(auto& p : pile) {
|
|
||||||
if(idx == areacache.size()) {
|
|
||||||
areacache.emplace_back(sl::area(p));
|
|
||||||
if(isBig(areacache[idx]))
|
|
||||||
spatindex.insert({sl::boundingBox(p), idx});
|
|
||||||
}
|
|
||||||
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Candidate item bounding box
|
// Candidate item bounding box
|
||||||
auto ibb = item.boundingBox();
|
auto ibb = sl::boundingBox(item.transformedShape());
|
||||||
|
|
||||||
// Calculate the full bounding box of the pile with the candidate item
|
// Calculate the full bounding box of the pile with the candidate item
|
||||||
pile.emplace_back(item.transformedShape());
|
auto fullbb = boundingBox(pilebb, ibb);
|
||||||
auto fullbb = sl::boundingBox(pile);
|
|
||||||
pile.pop_back();
|
|
||||||
|
|
||||||
// The bounding box of the big items (they will accumulate in the center
|
// The bounding box of the big items (they will accumulate in the center
|
||||||
// of the pile
|
// of the pile
|
||||||
@ -189,10 +188,12 @@ objfunc(const PointImpl& bincenter,
|
|||||||
double density = 0;
|
double density = 0;
|
||||||
|
|
||||||
if(remaining.empty()) {
|
if(remaining.empty()) {
|
||||||
pile.emplace_back(item.transformedShape());
|
|
||||||
auto chull = sl::convexHull(pile);
|
auto mp = merged_pile;
|
||||||
pile.pop_back();
|
mp.emplace_back(item.transformedShape());
|
||||||
strategies::EdgeCache<PolygonImpl> ec(chull);
|
auto chull = sl::convexHull(mp);
|
||||||
|
|
||||||
|
placers::EdgeCache<PolygonImpl> ec(chull);
|
||||||
|
|
||||||
double circ = ec.circumference() / norm;
|
double circ = ec.circumference() / norm;
|
||||||
double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm;
|
double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm;
|
||||||
@ -201,16 +202,15 @@ objfunc(const PointImpl& bincenter,
|
|||||||
} else {
|
} else {
|
||||||
// Prepare a variable for the alignment score.
|
// Prepare a variable for the alignment score.
|
||||||
// This will indicate: how well is the candidate item aligned with
|
// This will indicate: how well is the candidate item aligned with
|
||||||
// its neighbors. We will check the aligment with all neighbors and
|
// its neighbors. We will check the alignment with all neighbors and
|
||||||
// return the score for the best alignment. So it is enough for the
|
// return the score for the best alignment. So it is enough for the
|
||||||
// candidate to be aligned with only one item.
|
// candidate to be aligned with only one item.
|
||||||
auto alignment_score = 1.0;
|
auto alignment_score = 1.0;
|
||||||
|
|
||||||
density = (fullbb.width()*fullbb.height()) / (norm*norm);
|
density = (fullbb.width()*fullbb.height()) / (norm*norm);
|
||||||
auto& trsh = item.transformedShape();
|
|
||||||
auto querybb = item.boundingBox();
|
auto querybb = item.boundingBox();
|
||||||
|
|
||||||
// Query the spatial index for the neigbours
|
// Query the spatial index for the neighbors
|
||||||
std::vector<SpatElement> result;
|
std::vector<SpatElement> result;
|
||||||
result.reserve(spatindex.size());
|
result.reserve(spatindex.size());
|
||||||
spatindex.query(bgi::intersects(querybb),
|
spatindex.query(bgi::intersects(querybb),
|
||||||
@ -218,10 +218,10 @@ objfunc(const PointImpl& bincenter,
|
|||||||
|
|
||||||
for(auto& e : result) { // now get the score for the best alignment
|
for(auto& e : result) { // now get the score for the best alignment
|
||||||
auto idx = e.second;
|
auto idx = e.second;
|
||||||
auto& p = pile[idx];
|
Item& p = items[idx];
|
||||||
auto parea = areacache[idx];
|
auto parea = p.area();
|
||||||
if(std::abs(1.0 - parea/item.area()) < 1e-6) {
|
if(std::abs(1.0 - parea/item.area()) < 1e-6) {
|
||||||
auto bb = sl::boundingBox(sl::Shapes<PolygonImpl>{p, trsh});
|
auto bb = boundingBox(p.boundingBox(), ibb);
|
||||||
auto bbarea = bb.area();
|
auto bbarea = bb.area();
|
||||||
auto ascore = 1.0 - (item.area() + parea)/bbarea;
|
auto ascore = 1.0 - (item.area() + parea)/bbarea;
|
||||||
|
|
||||||
@ -231,7 +231,7 @@ objfunc(const PointImpl& bincenter,
|
|||||||
|
|
||||||
// The final mix of the score is the balance between the distance
|
// The final mix of the score is the balance between the distance
|
||||||
// from the full pile center, the pack density and the
|
// from the full pile center, the pack density and the
|
||||||
// alignment with the neigbours
|
// alignment with the neighbors
|
||||||
if(result.empty())
|
if(result.empty())
|
||||||
score = 0.5 * dist + 0.5 * density;
|
score = 0.5 * dist + 0.5 * density;
|
||||||
else
|
else
|
||||||
@ -239,7 +239,6 @@ objfunc(const PointImpl& bincenter,
|
|||||||
}
|
}
|
||||||
} else if( !isBig(item.area()) && spatindex.empty()) {
|
} else if( !isBig(item.area()) && spatindex.empty()) {
|
||||||
auto bindist = pl::distance(ibb.center(), bincenter) / norm;
|
auto bindist = pl::distance(ibb.center(), bincenter) / norm;
|
||||||
|
|
||||||
// Bindist is surprisingly enough...
|
// Bindist is surprisingly enough...
|
||||||
score = bindist;
|
score = bindist;
|
||||||
} else {
|
} else {
|
||||||
@ -271,7 +270,7 @@ void fillConfig(PConf& pcfg) {
|
|||||||
// Goes from 0.0 to 1.0 and scales performance as well
|
// Goes from 0.0 to 1.0 and scales performance as well
|
||||||
pcfg.accuracy = 0.65f;
|
pcfg.accuracy = 0.65f;
|
||||||
|
|
||||||
pcfg.parallel = false;
|
pcfg.parallel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class TBin>
|
template<class TBin>
|
||||||
@ -280,7 +279,8 @@ class AutoArranger {};
|
|||||||
template<class TBin>
|
template<class TBin>
|
||||||
class _ArrBase {
|
class _ArrBase {
|
||||||
protected:
|
protected:
|
||||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>;
|
|
||||||
|
using Placer = TPacker<TBin>;
|
||||||
using Selector = FirstFitSelection;
|
using Selector = FirstFitSelection;
|
||||||
using Packer = Nester<Placer, Selector>;
|
using Packer = Nester<Placer, Selector>;
|
||||||
using PConfig = typename Packer::PlacementConfig;
|
using PConfig = typename Packer::PlacementConfig;
|
||||||
@ -290,10 +290,12 @@ protected:
|
|||||||
Packer pck_;
|
Packer pck_;
|
||||||
PConfig pconf_; // Placement configuration
|
PConfig pconf_; // Placement configuration
|
||||||
double bin_area_;
|
double bin_area_;
|
||||||
std::vector<double> areacache_;
|
|
||||||
SpatIndex rtree_;
|
SpatIndex rtree_;
|
||||||
double norm_;
|
double norm_;
|
||||||
Pile pile_cache_;
|
Pile merged_pile_;
|
||||||
|
Box pilebb_;
|
||||||
|
ItemGroup remaining_;
|
||||||
|
ItemGroup items_;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
_ArrBase(const TBin& bin, Distance dist,
|
_ArrBase(const TBin& bin, Distance dist,
|
||||||
@ -302,11 +304,35 @@ public:
|
|||||||
norm_(std::sqrt(sl::area(bin)))
|
norm_(std::sqrt(sl::area(bin)))
|
||||||
{
|
{
|
||||||
fillConfig(pconf_);
|
fillConfig(pconf_);
|
||||||
|
|
||||||
|
pconf_.before_packing =
|
||||||
|
[this](const Pile& merged_pile, // merged pile
|
||||||
|
const ItemGroup& items, // packed items
|
||||||
|
const ItemGroup& remaining) // future items to be packed
|
||||||
|
{
|
||||||
|
items_ = items;
|
||||||
|
merged_pile_ = merged_pile;
|
||||||
|
remaining_ = remaining;
|
||||||
|
|
||||||
|
pilebb_ = sl::boundingBox(merged_pile);
|
||||||
|
|
||||||
|
rtree_.clear();
|
||||||
|
|
||||||
|
// We will treat big items (compared to the print bed) differently
|
||||||
|
auto isBig = [this](double a) {
|
||||||
|
return a/bin_area_ > BIG_ITEM_TRESHOLD ;
|
||||||
|
};
|
||||||
|
|
||||||
|
for(unsigned idx = 0; idx < items.size(); ++idx) {
|
||||||
|
Item& itm = items[idx];
|
||||||
|
if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pck_.progressIndicator(progressind);
|
pck_.progressIndicator(progressind);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
|
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
|
||||||
areacache_.clear();
|
|
||||||
rtree_.clear();
|
rtree_.clear();
|
||||||
return pck_.executeIndexed(std::forward<Args>(args)...);
|
return pck_.executeIndexed(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
@ -320,26 +346,28 @@ public:
|
|||||||
std::function<void(unsigned)> progressind):
|
std::function<void(unsigned)> progressind):
|
||||||
_ArrBase<Box>(bin, dist, progressind)
|
_ArrBase<Box>(bin, dist, progressind)
|
||||||
{
|
{
|
||||||
// pconf_.object_function = [this, bin] (
|
|
||||||
// const Pile& pile_c,
|
|
||||||
// const Item &item,
|
|
||||||
// const ItemGroup& rem) {
|
|
||||||
|
|
||||||
// auto& pile = pile_cache_;
|
pconf_.object_function = [this, bin] (const Item &item) {
|
||||||
// if(pile.size() != pile_c.size()) pile = pile_c;
|
|
||||||
|
|
||||||
// auto result = objfunc(bin.center(), bin_area_, pile,
|
auto result = objfunc(bin.center(),
|
||||||
// item, norm_, areacache_, rtree_, rem);
|
merged_pile_,
|
||||||
// double score = std::get<0>(result);
|
pilebb_,
|
||||||
// auto& fullbb = std::get<1>(result);
|
items_,
|
||||||
|
item,
|
||||||
|
bin_area_,
|
||||||
|
norm_,
|
||||||
|
rtree_,
|
||||||
|
remaining_);
|
||||||
|
|
||||||
// auto wdiff = fullbb.width() - bin.width();
|
double score = std::get<0>(result);
|
||||||
// auto hdiff = fullbb.height() - bin.height();
|
auto& fullbb = std::get<1>(result);
|
||||||
// if(wdiff > 0) score += std::pow(wdiff, 2) / norm_;
|
|
||||||
// if(hdiff > 0) score += std::pow(hdiff, 2) / norm_;
|
|
||||||
|
|
||||||
// return score;
|
double miss = Placer::overfit(fullbb, bin);
|
||||||
// };
|
miss = miss > 0? miss : 0;
|
||||||
|
score += miss*miss;
|
||||||
|
|
||||||
|
return score;
|
||||||
|
};
|
||||||
|
|
||||||
pck_.configure(pconf_);
|
pck_.configure(pconf_);
|
||||||
}
|
}
|
||||||
@ -355,36 +383,31 @@ public:
|
|||||||
std::function<void(unsigned)> progressind):
|
std::function<void(unsigned)> progressind):
|
||||||
_ArrBase<lnCircle>(bin, dist, progressind) {
|
_ArrBase<lnCircle>(bin, dist, progressind) {
|
||||||
|
|
||||||
pconf_.object_function = [this, &bin] (
|
pconf_.object_function = [this, &bin] (const Item &item) {
|
||||||
const Pile& pile_c,
|
|
||||||
const Item &item,
|
|
||||||
const ItemGroup& rem) {
|
|
||||||
|
|
||||||
auto& pile = pile_cache_;
|
auto result = objfunc(bin.center(),
|
||||||
if(pile.size() != pile_c.size()) pile = pile_c;
|
merged_pile_,
|
||||||
|
pilebb_,
|
||||||
|
items_,
|
||||||
|
item,
|
||||||
|
bin_area_,
|
||||||
|
norm_,
|
||||||
|
rtree_,
|
||||||
|
remaining_);
|
||||||
|
|
||||||
auto result = objfunc(bin.center(), bin_area_, pile, item, norm_,
|
|
||||||
areacache_, rtree_, rem);
|
|
||||||
double score = std::get<0>(result);
|
double score = std::get<0>(result);
|
||||||
auto& fullbb = std::get<1>(result);
|
|
||||||
|
|
||||||
auto d = pl::distance(fullbb.minCorner(),
|
auto isBig = [this](const Item& itm) {
|
||||||
fullbb.maxCorner());
|
return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ;
|
||||||
auto diff = d - 2*bin.radius();
|
};
|
||||||
|
|
||||||
if(diff > 0) {
|
if(isBig(item)) {
|
||||||
if( item.area() > 0.01*bin_area_ && item.vertexCount() < 30) {
|
auto mp = merged_pile_;
|
||||||
pile.emplace_back(item.transformedShape());
|
mp.push_back(item.transformedShape());
|
||||||
auto chull = sl::convexHull(pile);
|
auto chull = sl::convexHull(mp);
|
||||||
pile.pop_back();
|
double miss = Placer::overfit(chull, bin);
|
||||||
|
if(miss < 0) miss = 0;
|
||||||
auto C = strategies::boundingCircle(chull);
|
score += miss*miss;
|
||||||
auto rdiff = C.radius() - bin.radius();
|
|
||||||
|
|
||||||
if(rdiff > 0) {
|
|
||||||
score += std::pow(rdiff, 3) / norm_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
@ -401,17 +424,18 @@ public:
|
|||||||
std::function<void(unsigned)> progressind):
|
std::function<void(unsigned)> progressind):
|
||||||
_ArrBase<PolygonImpl>(bin, dist, progressind)
|
_ArrBase<PolygonImpl>(bin, dist, progressind)
|
||||||
{
|
{
|
||||||
pconf_.object_function = [this, &bin] (
|
pconf_.object_function = [this, &bin] (const Item &item) {
|
||||||
const Pile& pile_c,
|
|
||||||
const Item &item,
|
|
||||||
const ItemGroup& rem) {
|
|
||||||
|
|
||||||
auto& pile = pile_cache_;
|
|
||||||
if(pile.size() != pile_c.size()) pile = pile_c;
|
|
||||||
|
|
||||||
auto binbb = sl::boundingBox(bin);
|
auto binbb = sl::boundingBox(bin);
|
||||||
auto result = objfunc(binbb.center(), bin_area_, pile, item, norm_,
|
auto result = objfunc(binbb.center(),
|
||||||
areacache_, rtree_, rem);
|
merged_pile_,
|
||||||
|
pilebb_,
|
||||||
|
items_,
|
||||||
|
item,
|
||||||
|
bin_area_,
|
||||||
|
norm_,
|
||||||
|
rtree_,
|
||||||
|
remaining_);
|
||||||
double score = std::get<0>(result);
|
double score = std::get<0>(result);
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
@ -428,16 +452,17 @@ public:
|
|||||||
AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
|
AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
|
||||||
_ArrBase<Box>(Box(0, 0), dist, progressind)
|
_ArrBase<Box>(Box(0, 0), dist, progressind)
|
||||||
{
|
{
|
||||||
this->pconf_.object_function = [this] (
|
this->pconf_.object_function = [this] (const Item &item) {
|
||||||
const Pile& pile_c,
|
|
||||||
const Item &item,
|
|
||||||
const ItemGroup& rem) {
|
|
||||||
|
|
||||||
auto& pile = pile_cache_;
|
auto result = objfunc({0, 0},
|
||||||
if(pile.size() != pile_c.size()) pile = pile_c;
|
merged_pile_,
|
||||||
|
pilebb_,
|
||||||
auto result = objfunc({0, 0}, 0, pile, item, norm_,
|
items_,
|
||||||
areacache_, rtree_, rem);
|
item,
|
||||||
|
0,
|
||||||
|
norm_,
|
||||||
|
rtree_,
|
||||||
|
remaining_);
|
||||||
return std::get<0>(result);
|
return std::get<0>(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user