Parallel placer now works with the custom Slic3r object function. Works an order of magnitude faster.

This commit is contained in:
tamasmeszaros 2018-08-22 13:52:41 +02:00
parent 8617b0a409
commit e522ad1a00
18 changed files with 739 additions and 296 deletions

View File

@ -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})

View File

@ -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})

View 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 havent found, or
# dont 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()

View File

@ -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;
} }

View File

@ -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>;
} }

View File

@ -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;
} }
/** /**

View File

@ -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 {

View File

@ -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 {};

View File

@ -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),

View File

@ -3,7 +3,7 @@
#include "../libnest2d.hpp" #include "../libnest2d.hpp"
namespace libnest2d { namespace strategies { namespace libnest2d { namespace placers {
struct EmptyConfig {}; struct EmptyConfig {};

View File

@ -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]\

View File

@ -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> {

View File

@ -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> {

View File

@ -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 {

View File

@ -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)));

View File

@ -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()};
//}
} }
} }

View File

@ -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)};
} }
}; };

View File

@ -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);
}; };