diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index d41b4c13a..70e933058 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -729,6 +729,7 @@ add_custom_target(pot set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") add_subdirectory(${LIBDIR}/libnest2d) +target_compile_definitions(libslic3r PUBLIC -DUSE_TBB) target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES}) target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES}) diff --git a/xs/src/libnest2d/CMakeLists.txt b/xs/src/libnest2d/CMakeLists.txt index cd3e35b97..f81355012 100644 --- a/xs/src/libnest2d/CMakeLists.txt +++ b/xs/src/libnest2d/CMakeLists.txt @@ -90,6 +90,7 @@ if(LIBNEST2D_UNITTESTS) endif() if(LIBNEST2D_BUILD_EXAMPLES) + add_executable(example examples/main.cpp # tools/libnfpglue.hpp # tools/libnfpglue.cpp @@ -98,8 +99,30 @@ if(LIBNEST2D_BUILD_EXAMPLES) tools/svgtools.hpp tests/printer_parts.cpp 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_include_directories(example PUBLIC ${LIBNEST2D_HEADERS}) diff --git a/xs/src/libnest2d/cmake_modules/FindTBB.cmake b/xs/src/libnest2d/cmake_modules/FindTBB.cmake new file mode 100644 index 000000000..8b498d3ab --- /dev/null +++ b/xs/src/libnest2d/cmake_modules/FindTBB.cmake @@ -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 - The path of the TBB the corresponding TBB library. +# These libraries, if specified, override the +# corresponding library search results, where +# 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__FOUND - If False, optional 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_RELEASE - The path of the TBB release version of +# , where may be tbb, tbb_debug, +# tbbmalloc, tbbmalloc_debug, tbb_preview, or +# tbb_preview_debug. +# * TBB__LIBRARY_DEGUG - The path of the TBB release version of +# , where 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 /gcc4.1 or + # /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 "$<$,$>: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() diff --git a/xs/src/libnest2d/examples/main.cpp b/xs/src/libnest2d/examples/main.cpp index 57be7a208..ebc3fb15c 100644 --- a/xs/src/libnest2d/examples/main.cpp +++ b/xs/src/libnest2d/examples/main.cpp @@ -54,7 +54,7 @@ void arrangeRectangles() { const int SCALE = 1000000; - std::vector rects(100, { + std::vector rects(202, { {-9945219, -3065619}, {-9781479, -2031780}, {-9510560, -1020730}, @@ -104,8 +104,6 @@ void arrangeRectangles() { // input.insert(input.end(), prusaExParts().begin(), prusaExParts().end()); // input.insert(input.end(), stegoParts().begin(), stegoParts().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); // PolygonImpl bin = { @@ -123,11 +121,11 @@ void arrangeRectangles() { // {} // }; -// _Circle bin({0, 0}, 125*SCALE); +// Circle bin({0, 0}, 125*SCALE); - auto min_obj_distance = static_cast(1.5*SCALE); + auto min_obj_distance = static_cast(6*SCALE); - using Placer = strategies::_NofitPolyPlacer; + using Placer = placers::_NofitPolyPlacer; using Packer = Nester; Packer arrange(bin, min_obj_distance); @@ -136,7 +134,7 @@ void arrangeRectangles() { pconf.alignment = Placer::Config::Alignment::CENTER; pconf.starting_point = Placer::Config::Alignment::CENTER; pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/}; - pconf.accuracy = 0.5f; + pconf.accuracy = 0.65f; pconf.parallel = true; Packer::SelectionConfig sconf; @@ -149,12 +147,6 @@ void arrangeRectangles() { arrange.configure(pconf, sconf); 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; }); @@ -201,10 +193,10 @@ void arrangeRectangles() { for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); } std::cout << ") Total: " << total << std::endl; - for(auto& it : input) { - auto ret = sl::isValid(it.transformedShape()); - std::cout << ret.second << std::endl; - } +// for(auto& it : input) { +// auto ret = sl::isValid(it.transformedShape()); +// std::cout << ret.second << std::endl; +// } if(total != input.size()) std::cout << "ERROR " << "could not pack " << input.size() - total << " elements!" @@ -222,7 +214,5 @@ void arrangeRectangles() { int main(void /*int argc, char **argv*/) { arrangeRectangles(); -//// findDegenerateCase(); - return EXIT_SUCCESS; } diff --git a/xs/src/libnest2d/libnest2d.h b/xs/src/libnest2d/libnest2d.h index 05677afd7..bfd88f4f5 100644 --- a/xs/src/libnest2d/libnest2d.h +++ b/xs/src/libnest2d/libnest2d.h @@ -30,12 +30,12 @@ using Rectangle = _Rectangle; using PackGroup = _PackGroup; using IndexedPackGroup = _IndexedPackGroup; -using FillerSelection = strategies::_FillerSelection; -using FirstFitSelection = strategies::_FirstFitSelection; -using DJDHeuristic = strategies::_DJDHeuristic; +using FillerSelection = selections::_FillerSelection; +using FirstFitSelection = selections::_FirstFitSelection; +using DJDHeuristic = selections::_DJDHeuristic; -using NfpPlacer = strategies::_NofitPolyPlacer; -using BottomLeftPlacer = strategies::_BottomLeftPlacer; +using NfpPlacer = placers::_NofitPolyPlacer; +using BottomLeftPlacer = placers::_BottomLeftPlacer; } diff --git a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp index b9dfd2185..2982454cd 100644 --- a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp +++ b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp @@ -102,7 +102,7 @@ inline TPoint leftmostDownVertex(const RawShape& sh) auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh), __nfp::_vsort); - return *it; + return it == shapelike::cend(sh) ? TPoint() : *it;; } /** @@ -118,7 +118,7 @@ TPoint rightmostUpVertex(const RawShape& sh) auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh), __nfp::_vsort); - return *it; + return it == shapelike::cend(sh) ? TPoint() : *it; } /** diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp index 42255cbb4..4d1e62f99 100644 --- a/xs/src/libnest2d/libnest2d/libnest2d.hpp +++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp @@ -65,8 +65,8 @@ class _Item { mutable VertexConstIterator lmb_; // leftmost bottom vertex mutable bool rmt_valid_ = false, lmb_valid_ = false; mutable struct BBCache { - Box bb; bool valid; Vertex tr; - BBCache(): valid(false), tr(0, 0) {} + Box bb; bool valid; + BBCache(): valid(false) {} } bb_cache_; public: @@ -310,7 +310,7 @@ public: { if(translation_ != tr) { 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 { if(!bb_cache_.valid) { - bb_cache_.bb = sl::boundingBox(transformedShape()); - bb_cache_.tr = {0, 0}; + if(!has_rotation_) + 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; } - auto &bb = bb_cache_.bb; auto &tr = bb_cache_.tr; - return {bb.minCorner() + tr, bb.maxCorner() + tr}; + auto &bb = bb_cache_.bb; auto &tr = translation_; + return {bb.minCorner() + tr, bb.maxCorner() + tr }; } inline Vertex referenceVertex() const { diff --git a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp index 0ba9eb3c0..18c27c40c 100644 --- a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp @@ -5,7 +5,7 @@ #include "placer_boilerplate.hpp" -namespace libnest2d { namespace strategies { +namespace libnest2d { namespace placers { template struct Epsilon {}; diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp index b9e0ba8f1..c86fb507e 100644 --- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp @@ -21,6 +21,12 @@ #include "tools/svgtools.hpp" +#ifdef USE_TBB +#include +#elif defined(_OPENMP) +#include +#endif + namespace libnest2d { namespace __parallel { @@ -33,20 +39,52 @@ using TIteratorValue = typename iterator_traits::value_type; template inline void enumerate( Iterator from, Iterator to, - function, unsigned)> fn, + function, size_t)> fn, 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(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> rets(N); auto it = from; - for(unsigned b = 0; b < N; b++) { - rets[b] = std::async(policy, fn, *it++, b); + for(TN b = 0; b < N; 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 { @@ -98,7 +136,7 @@ using Hash = std::unordered_map>; } -namespace strategies { +namespace placers { template struct NfpPConfig { @@ -134,30 +172,12 @@ struct NfpPConfig { * that will optimize for the best pack efficiency. With a custom fitting * function you can e.g. influence the shape of the arranged pile. * - * \param shapes The first parameter is a container with all the placed - * polygons excluding the current candidate. You can calculate a bounding - * box or convex hull on this pile of polygons without the candidate item - * 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). + * \param item The only parameter is the candidate item which has info + * about its current position. Your job is to rate this position compared to + * the already packed items. * */ - std::function&, const _Item&, - const ItemGroup&)> - object_function; + std::function&)> object_function; /** * @brief The quality of search for an optimal placement. @@ -180,6 +200,34 @@ struct NfpPConfig { */ 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&, // merged pile + const ItemGroup&, // packed items + const ItemGroup& // remaining items + )> before_packing; + NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}), alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {} }; @@ -428,7 +476,7 @@ Circle minimizeCircle(const RawShape& sh) { opt::StopCriteria stopcr; - stopcr.max_iterations = 100; + stopcr.max_iterations = 30; stopcr.relative_score_difference = 1e-3; opt::TOptimizer solver(stopcr); @@ -590,35 +638,25 @@ private: { using namespace nfp; - Shapes nfps; + Shapes nfps(items_.size()); const Item& trsh = itsh.first; - // nfps.reserve(polygons.size()); - -// unsigned idx = 0; - for(Item& sh : items_) { - -// auto ik = item_keys_[idx++] + itsh.second; -// auto fnd = nfpcache_.find(ik); - -// nfp::NfpResult subnfp_r; -// if(fnd == nfpcache_.end()) { - - auto subnfp_r = noFitPolygon( - sh.transformedShape(), trsh.transformedShape()); -// nfpcache_[ik] = subnfp_r; -// } else { -// subnfp_r = fnd->second; -// } + __parallel::enumerate(items_.begin(), items_.end(), + [&nfps, &trsh](const Item& sh, size_t n) + { + auto& fixedp = sh.transformedShape(); + auto& orbp = trsh.transformedShape(); + auto subnfp_r = noFitPolygon(fixedp, orbp); correctNfpPosition(subnfp_r, sh, trsh); + nfps[n] = subnfp_r.first; + }); - // nfps.emplace_back(subnfp_r.first); - nfps = nfp::merge(nfps, subnfp_r.first); - } +// for(auto& n : nfps) { +// auto valid = sl::isValid(n); +// if(!valid.first) std::cout << "Warning: " << valid.second << std::endl; +// } - // nfps = nfp::merge(nfps); - - return nfps; + return nfp::merge(nfps); } template @@ -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; template> @@ -810,6 +863,7 @@ private: item.translation(initial_tr); item.rotation(initial_rot + rot); + item.boundingBox(); // fill the bb cache // place the new item outside of the print bed to make sure // it is disjunct from the current merged pile @@ -840,21 +894,17 @@ private: auto merged_pile = nfp::merge(pile); auto& bin = bin_; 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 // customizable by the library client auto _objfunc = config_.object_function? config_.object_function : - [norm, /*pile_area,*/ bin, merged_pile]( - const Pile& /*pile*/, - const Item& item, - const ItemGroup& /*remaining*/) + [norm, bin, binbb, pbb](const Item& item) { auto ibb = item.boundingBox(); - auto binbb = sl::boundingBox(bin); - auto mp = merged_pile; - mp.emplace_back(item.transformedShape()); - auto fullbb = sl::boundingBox(mp); + auto fullbb = boundingBox(pbb, ibb); double score = pl::distance(ibb.center(), binbb.center()); score /= norm; @@ -868,15 +918,12 @@ private: // Our object function for placement auto rawobjfunc = - [item, _objfunc, iv, - startpos, remlist, pile] (Vertex v) + [_objfunc, iv, startpos] (Vertex v, Item& itm) { auto d = v - iv; d += startpos; - Item itm = item; itm.translation(d); - - return _objfunc(pile, itm, remlist); + return _objfunc(itm); }; auto getNfpPoint = [&ecache](const Optimum& opt) @@ -906,6 +953,9 @@ private: std::launch policy = std::launch::deferred; if(config_.parallel) policy |= std::launch::async; + if(config_.before_packing) + config_.before_packing(merged_pile, items_, remlist); + using OptResult = opt::Result; using OptResults = std::vector; @@ -914,21 +964,27 @@ private: for(unsigned ch = 0; ch < ecache.size(); ch++) { auto& cache = ecache[ch]; - auto contour_ofn = [rawobjfunc, getNfpPoint, ch] - (double relpos) - { - return rawobjfunc(getNfpPoint(Optimum(relpos, ch))); - }; - OptResults results(cache.corners().size()); + auto& rofn = rawobjfunc; + auto& nfpoint = getNfpPoint; + __parallel::enumerate( cache.corners().begin(), cache.corners().end(), - [&contour_ofn, &results] - (double pos, unsigned n) + [&results, &item, &rofn, &nfpoint, ch] + (double pos, size_t n) { Optimizer solver; + + Item itemcpy = item; + auto contour_ofn = [&rofn, &nfpoint, ch, &itemcpy] + (double relpos) + { + Optimum op(relpos, ch); + return rofn(nfpoint(op), itemcpy); + }; + try { results[n] = solver.optimize_min(contour_ofn, opt::initvals(pos), @@ -959,24 +1015,27 @@ private: } 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.resize(cache.corners(hidx).size()); // TODO : use parallel for __parallel::enumerate(cache.corners(hidx).begin(), cache.corners(hidx).end(), - [&hole_ofn, &results] - (double pos, unsigned n) + [&results, &item, &nfpoint, + &rofn, ch, hidx] + (double pos, size_t n) { 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 { results[n] = solver.optimize_min(hole_ofn, opt::initvals(pos), diff --git a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp index 44e2bc1b0..0df1b8c91 100644 --- a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp +++ b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp @@ -3,7 +3,7 @@ #include "../libnest2d.hpp" -namespace libnest2d { namespace strategies { +namespace libnest2d { namespace placers { struct EmptyConfig {}; diff --git a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp index 846b00bad..ee93d0592 100644 --- a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp +++ b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp @@ -8,7 +8,7 @@ #include "selection_boilerplate.hpp" -namespace libnest2d { namespace strategies { +namespace libnest2d { namespace selections { /** * Selection heuristic based on [López-Camacho]\ diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/xs/src/libnest2d/libnest2d/selections/filler.hpp index b20455b0e..0da7220a1 100644 --- a/xs/src/libnest2d/libnest2d/selections/filler.hpp +++ b/xs/src/libnest2d/libnest2d/selections/filler.hpp @@ -3,7 +3,7 @@ #include "selection_boilerplate.hpp" -namespace libnest2d { namespace strategies { +namespace libnest2d { namespace selections { template class _FillerSelection: public SelectionBoilerplate { diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp index eb820a518..bca7497db 100644 --- a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp +++ b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp @@ -4,7 +4,7 @@ #include "../libnest2d.hpp" #include "selection_boilerplate.hpp" -namespace libnest2d { namespace strategies { +namespace libnest2d { namespace selections { template class _FirstFitSelection: public SelectionBoilerplate { diff --git a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp index 59ef5cb23..05bbae658 100644 --- a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp +++ b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp @@ -3,8 +3,7 @@ #include "../libnest2d.hpp" -namespace libnest2d { -namespace strategies { +namespace libnest2d { namespace selections { template class SelectionBoilerplate { diff --git a/xs/src/libnest2d/tests/test.cpp b/xs/src/libnest2d/tests/test.cpp index b85bbc111..323fb8d31 100644 --- a/xs/src/libnest2d/tests/test.cpp +++ b/xs/src/libnest2d/tests/test.cpp @@ -102,17 +102,17 @@ TEST(BasicFunctionality, creationAndDestruction) TEST(GeometryAlgorithms, boundingCircle) { using namespace libnest2d; - using strategies::boundingCircle; + using placers::boundingCircle; PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}}; - _Circle c = boundingCircle(p); + Circle c = boundingCircle(p); ASSERT_EQ(c.center().X, 0); ASSERT_EQ(c.center().Y, 0); ASSERT_DOUBLE_EQ(c.radius(), 10); shapelike::translate(p, PointImpl{10, 10}); - c = boundingCircle(p); + c = boundingCircle(p); ASSERT_EQ(c.center().X, 10); ASSERT_EQ(c.center().Y, 10); @@ -712,7 +712,7 @@ void testNfp(const std::vector& testdata) { auto& exportfun = exportSVG; - auto onetest = [&](Item& orbiter, Item& stationary){ + auto onetest = [&](Item& orbiter, Item& stationary, unsigned testidx){ testcase++; orbiter.translate({210*SCALE, 0}); @@ -720,15 +720,19 @@ void testNfp(const std::vector& testdata) { auto&& nfp = nfp::noFitPolygon(stationary.rawShape(), 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) { - std::cout << v.second << std::endl; - } + /*Item infp(nfp.first); + if(!valid.first) { + std::cout << "test instance: " << testidx << " " + << valid.second << std::endl; + std::vector> inp = {std::ref(infp)}; + exportfun(inp, bin, testidx); + }*/ - ASSERT_TRUE(v.first); + ASSERT_TRUE(valid.first); Item infp(nfp.first); @@ -748,7 +752,7 @@ void testNfp(const std::vector& testdata) { bool touching = Item::touches(tmp, stationary); - if(!touching) { + if(!touching || !valid.first) { std::vector> inp = { std::ref(stationary), std::ref(tmp), std::ref(infp) }; @@ -760,16 +764,18 @@ void testNfp(const std::vector& testdata) { } }; + unsigned tidx = 0; for(auto& td : testdata) { auto orbiter = td.orbiter; auto stationary = td.stationary; - onetest(orbiter, stationary); + onetest(orbiter, stationary, tidx++); } + tidx = 0; for(auto& td : testdata) { auto orbiter = td.stationary; auto stationary = td.orbiter; - onetest(orbiter, stationary); + onetest(orbiter, stationary, tidx++); } } } @@ -796,7 +802,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) { Rectangle input(10, 10); - strategies::EdgeCache ecache(input); + placers::EdgeCache ecache(input); auto first = *input.begin(); ASSERT_TRUE(getX(first) == getX(ecache.coords(0))); diff --git a/xs/src/libnest2d/tools/nfp_svgnest.hpp b/xs/src/libnest2d/tools/nfp_svgnest.hpp index 8ab571c00..ac5700c10 100644 --- a/xs/src/libnest2d/tools/nfp_svgnest.hpp +++ b/xs/src/libnest2d/tools/nfp_svgnest.hpp @@ -32,15 +32,18 @@ template struct _alg { #define dNAN std::nan("") struct Vector { - Coord x, y; + Coord x = 0.0, y = 0.0; bool marked = false; Vector() = default; 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)}; } Vector& operator=(const Point& p) { 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 il): x(*il.begin()), y(*std::next(il.begin())) {} }; @@ -58,8 +61,10 @@ template struct _alg { v_.reserve(c.size()); std::transform(c.begin(), c.end(), std::back_inserter(v_), [](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; @@ -67,8 +72,10 @@ template struct _alg { Coord offsety = 0; size_t size() const { return v_.size(); } bool empty() const { return v_.empty(); } - typename Contour::const_iterator begin() const { return v_.cbegin(); } - typename Contour::const_iterator end() const { return v_.cend(); } + typename std::vector::const_iterator cbegin() const { return v_.cbegin(); } + typename std::vector::const_iterator cend() const { return v_.cend(); } + typename std::vector::iterator begin() { return v_.begin(); } + typename std::vector::iterator end() { return v_.end(); } Vector& operator[](size_t idx) { return v_[idx]; } const Vector& operator[](size_t idx) const { return v_[idx]; } template @@ -83,12 +90,16 @@ template struct _alg { operator Contour() const { Contour cnt; - cnt.reserve(v_.size()); + cnt.reserve(v_.size() + 1); std::transform(v_.begin(), v_.end(), std::back_inserter(cnt), [](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(cnt); + +// std::reverse(cnt.begin(), cnt.end()); + return shapelike::getContour(sh); } }; @@ -235,11 +246,11 @@ template struct _alg { if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) && (pdotnorm>s1dotnorm && pdotnorm>s2dotnorm)) { - return double(min(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm)); + return min(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm); } if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) && (pdotnorm struct _alg { auto EFmin = min(dotE, dotF); // 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; } // segments miss eachother completely @@ -362,7 +373,7 @@ template struct _alg { d = dNAN; } } - if(isnan(d)){ + if(!isnan(d)){ distances.emplace_back(d); } } @@ -392,7 +403,7 @@ template struct _alg { auto d = pointDistance(E,A,B,direction); if(!isnan(d) && _almostEqual(d, 0)) { // crossF struct _alg { if(!isnan(d) && _almostEqual(d, 0)) { // && crossE struct _alg { return *std::min_element(distances.begin(), distances.end()); } - static double polygonSlideDistance( const Cntr& A, - const Cntr& B, + static double polygonSlideDistance( const Cntr& AA, + const Cntr& BB, Vector direction, bool ignoreNegative) { // Vector A1, A2, B1, B2; + Cntr A = AA; + Cntr B = BB; + Coord Aoffsetx = A.offsetx; Coord Boffsetx = B.offsetx; Coord Aoffsety = A.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& edgeB = B; @@ -457,7 +480,7 @@ template struct _alg { Vector A1 = {x(edgeA[j]) + Aoffsetx, y(edgeA[j]) + Aoffsety }; Vector A2 = {x(edgeA[j+1]) + Aoffsetx, y(edgeA[j+1]) + Aoffsety}; 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)) || (_almostEqual(B1.x, B2.x) && _almostEqual(B1.y, B2.y))){ @@ -476,23 +499,26 @@ template struct _alg { return distance; } - static double polygonProjectionDistance(const Cntr& A, - const Cntr& B, + static double polygonProjectionDistance(const Cntr& AA, + const Cntr& BB, Vector direction) { + Cntr A = AA; + Cntr B = BB; + auto Boffsetx = B.offsetx; auto Boffsety = B.offsety; auto Aoffsetx = A.offsetx; auto Aoffsety = A.offsety; // close the loop for polygons - /*if(A[0] != A[A.length-1]){ + if(A[0] != A[A.size()-1]){ A.push(A[0]); } - if(B[0] != B[B.length-1]){ + if(B[0] != B[B.size()-1]){ B.push(B[0]); - }*/ + } auto& edgeA = A; auto& edgeB = B; @@ -665,7 +691,7 @@ template struct _alg { A.offsetx = 0; A.offsety = 0; - unsigned i = 0, j = 0; + long i = 0, j = 0; auto minA = y(A[0]); long minAindex = 0; @@ -709,7 +735,8 @@ template struct _alg { struct Touch { int type; - long A, B; + long A; + long B; Touch(int t, long a, long b): type(t), A(a), B(b) {} }; @@ -721,6 +748,15 @@ template struct _alg { // maintain a list of touching points/edges std::vector 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; NFP.emplace_back(x(B[0]) + B.offsetx, y(B[0]) + B.offsety); @@ -736,10 +772,10 @@ template struct _alg { // find touching vertices/edges 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++){ - 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) && _almostEqual(A[i].y, B[j].y+B.offsety)) @@ -762,15 +798,6 @@ template 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 std::vector vectors; for(i=0; i < touching.size(); i++){ @@ -871,7 +898,6 @@ template struct _alg { // will cause immediate intersection. For now just check them all V translate = {0, 0, nullptr, nullptr}; - V prevvector = {0, 0, nullptr, nullptr}; double maxd = 0; for(i = 0; i < vectors.size(); i++) { @@ -897,7 +923,8 @@ template 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; if(isnan(d) || d*d > vecd2){ @@ -985,19 +1012,6 @@ template struct _alg { template const double _alg::TOL = std::pow(10, -9); -//template -//nfp::NfpResult nfpSimpleSimple(const S& stat, const S& orb) { -//// using Cntr = TContour; -// using Point = TPoint; -//// using Coord = TCoord; -//// using Shapes = nfp::Shapes; - -// namespace sl = shapelike; - -// noFitPolygon(sl::getContour(stat), sl::getContour(orb), true, true); -// return {S(), Point()}; -//} - } } diff --git a/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp b/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp index 7ceb2d24d..ea1fb4d07 100644 --- a/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp +++ b/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp @@ -25,13 +25,11 @@ template<> struct NfpImpl { namespace sl = shapelike; using alg = __svgnest::_alg; - std::cout << "Itt vagyok" << std::endl; auto nfp_p = alg::noFitPolygon(sl::getContour(sh), sl::getContour(cother), false, false); PolygonImpl nfp_cntr; - nfp_cntr.Contour = nfp_p.front(); - std::cout << "Contour size: " << nfp_cntr.Contour.size() << std::endl; + if(!nfp_p.empty()) nfp_cntr.Contour = nfp_p.front(); return {nfp_cntr, referenceVertex(nfp_cntr)}; } }; diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index dcb0da9e5..618230cb7 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -100,55 +100,54 @@ namespace bgi = boost::geometry::index; using SpatElement = std::pair; using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; using ItemGroup = std::vector>; +template +using TPacker = typename placers::_NofitPolyPlacer; + +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 objfunc(const PointImpl& bincenter, - double bin_area, - sl::Shapes& pile, // The currently arranged pile + const shapelike::Shapes& merged_pile, + const Box& pilebb, + const ItemGroup& items, const Item &item, + double bin_area, double norm, // A norming factor for physical dimensions - std::vector& areacache, // pile item areas will be cached // a spatial index to quickly get neighbors of the candidate item - SpatIndex& spatindex, + const SpatIndex& spatindex, const ItemGroup& remaining ) { using Coord = TCoord; - static const double BIG_ITEM_TRESHOLD = 0.02; static const double ROUNDNESS_RATIO = 0.5; static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; // 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 ; }; - // 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 - auto ibb = item.boundingBox(); + auto ibb = sl::boundingBox(item.transformedShape()); // Calculate the full bounding box of the pile with the candidate item - pile.emplace_back(item.transformedShape()); - auto fullbb = sl::boundingBox(pile); - pile.pop_back(); + auto fullbb = boundingBox(pilebb, ibb); // The bounding box of the big items (they will accumulate in the center // of the pile @@ -189,10 +188,12 @@ objfunc(const PointImpl& bincenter, double density = 0; if(remaining.empty()) { - pile.emplace_back(item.transformedShape()); - auto chull = sl::convexHull(pile); - pile.pop_back(); - strategies::EdgeCache ec(chull); + + auto mp = merged_pile; + mp.emplace_back(item.transformedShape()); + auto chull = sl::convexHull(mp); + + placers::EdgeCache ec(chull); double circ = ec.circumference() / norm; double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm; @@ -201,16 +202,15 @@ objfunc(const PointImpl& bincenter, } else { // Prepare a variable for the alignment score. // 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 // candidate to be aligned with only one item. auto alignment_score = 1.0; density = (fullbb.width()*fullbb.height()) / (norm*norm); - auto& trsh = item.transformedShape(); auto querybb = item.boundingBox(); - // Query the spatial index for the neigbours + // Query the spatial index for the neighbors std::vector result; result.reserve(spatindex.size()); 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 auto idx = e.second; - auto& p = pile[idx]; - auto parea = areacache[idx]; + Item& p = items[idx]; + auto parea = p.area(); if(std::abs(1.0 - parea/item.area()) < 1e-6) { - auto bb = sl::boundingBox(sl::Shapes{p, trsh}); + auto bb = boundingBox(p.boundingBox(), ibb); auto bbarea = bb.area(); 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 // from the full pile center, the pack density and the - // alignment with the neigbours + // alignment with the neighbors if(result.empty()) score = 0.5 * dist + 0.5 * density; else @@ -239,7 +239,6 @@ objfunc(const PointImpl& bincenter, } } else if( !isBig(item.area()) && spatindex.empty()) { auto bindist = pl::distance(ibb.center(), bincenter) / norm; - // Bindist is surprisingly enough... score = bindist; } else { @@ -271,7 +270,7 @@ void fillConfig(PConf& pcfg) { // Goes from 0.0 to 1.0 and scales performance as well pcfg.accuracy = 0.65f; - pcfg.parallel = false; + pcfg.parallel = true; } template @@ -280,7 +279,8 @@ class AutoArranger {}; template class _ArrBase { protected: - using Placer = strategies::_NofitPolyPlacer; + + using Placer = TPacker; using Selector = FirstFitSelection; using Packer = Nester; using PConfig = typename Packer::PlacementConfig; @@ -290,10 +290,12 @@ protected: Packer pck_; PConfig pconf_; // Placement configuration double bin_area_; - std::vector areacache_; SpatIndex rtree_; double norm_; - Pile pile_cache_; + Pile merged_pile_; + Box pilebb_; + ItemGroup remaining_; + ItemGroup items_; public: _ArrBase(const TBin& bin, Distance dist, @@ -302,11 +304,35 @@ public: norm_(std::sqrt(sl::area(bin))) { 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); } template inline IndexedPackGroup operator()(Args&&...args) { - areacache_.clear(); rtree_.clear(); return pck_.executeIndexed(std::forward(args)...); } @@ -320,26 +346,28 @@ public: std::function progressind): _ArrBase(bin, dist, progressind) { -// pconf_.object_function = [this, bin] ( -// const Pile& pile_c, -// const Item &item, -// const ItemGroup& rem) { -// auto& pile = pile_cache_; -// if(pile.size() != pile_c.size()) pile = pile_c; + pconf_.object_function = [this, bin] (const Item &item) { -// auto result = objfunc(bin.center(), bin_area_, pile, -// item, norm_, areacache_, rtree_, rem); -// double score = std::get<0>(result); -// auto& fullbb = std::get<1>(result); + auto result = objfunc(bin.center(), + merged_pile_, + pilebb_, + items_, + item, + bin_area_, + norm_, + rtree_, + remaining_); -// auto wdiff = fullbb.width() - bin.width(); -// auto hdiff = fullbb.height() - bin.height(); -// if(wdiff > 0) score += std::pow(wdiff, 2) / norm_; -// if(hdiff > 0) score += std::pow(hdiff, 2) / norm_; + double score = std::get<0>(result); + auto& fullbb = std::get<1>(result); -// return score; -// }; + double miss = Placer::overfit(fullbb, bin); + miss = miss > 0? miss : 0; + score += miss*miss; + + return score; + }; pck_.configure(pconf_); } @@ -355,36 +383,31 @@ public: std::function progressind): _ArrBase(bin, dist, progressind) { - pconf_.object_function = [this, &bin] ( - const Pile& pile_c, - const Item &item, - const ItemGroup& rem) { + pconf_.object_function = [this, &bin] (const Item &item) { - auto& pile = pile_cache_; - if(pile.size() != pile_c.size()) pile = pile_c; + auto result = objfunc(bin.center(), + 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); - auto& fullbb = std::get<1>(result); - auto d = pl::distance(fullbb.minCorner(), - fullbb.maxCorner()); - auto diff = d - 2*bin.radius(); + auto isBig = [this](const Item& itm) { + return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ; + }; - if(diff > 0) { - if( item.area() > 0.01*bin_area_ && item.vertexCount() < 30) { - pile.emplace_back(item.transformedShape()); - auto chull = sl::convexHull(pile); - pile.pop_back(); - - auto C = strategies::boundingCircle(chull); - auto rdiff = C.radius() - bin.radius(); - - if(rdiff > 0) { - score += std::pow(rdiff, 3) / norm_; - } - } + if(isBig(item)) { + auto mp = merged_pile_; + mp.push_back(item.transformedShape()); + auto chull = sl::convexHull(mp); + double miss = Placer::overfit(chull, bin); + if(miss < 0) miss = 0; + score += miss*miss; } return score; @@ -401,17 +424,18 @@ public: std::function progressind): _ArrBase(bin, dist, progressind) { - pconf_.object_function = [this, &bin] ( - const Pile& pile_c, - const Item &item, - const ItemGroup& rem) { - - auto& pile = pile_cache_; - if(pile.size() != pile_c.size()) pile = pile_c; + pconf_.object_function = [this, &bin] (const Item &item) { auto binbb = sl::boundingBox(bin); - auto result = objfunc(binbb.center(), bin_area_, pile, item, norm_, - areacache_, rtree_, rem); + auto result = objfunc(binbb.center(), + merged_pile_, + pilebb_, + items_, + item, + bin_area_, + norm_, + rtree_, + remaining_); double score = std::get<0>(result); return score; @@ -428,16 +452,17 @@ public: AutoArranger(Distance dist, std::function progressind): _ArrBase(Box(0, 0), dist, progressind) { - this->pconf_.object_function = [this] ( - const Pile& pile_c, - const Item &item, - const ItemGroup& rem) { + this->pconf_.object_function = [this] (const Item &item) { - auto& pile = pile_cache_; - if(pile.size() != pile_c.size()) pile = pile_c; - - auto result = objfunc({0, 0}, 0, pile, item, norm_, - areacache_, rtree_, rem); + auto result = objfunc({0, 0}, + merged_pile_, + pilebb_, + items_, + item, + 0, + norm_, + rtree_, + remaining_); return std::get<0>(result); };