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

View File

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

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;
std::vector<Item> rects(100, {
std::vector<Item> 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<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>;
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;
}

View File

@ -30,12 +30,12 @@ using Rectangle = _Rectangle<PolygonImpl>;
using PackGroup = _PackGroup<PolygonImpl>;
using IndexedPackGroup = _IndexedPackGroup<PolygonImpl>;
using FillerSelection = strategies::_FillerSelection<PolygonImpl>;
using FirstFitSelection = strategies::_FirstFitSelection<PolygonImpl>;
using DJDHeuristic = strategies::_DJDHeuristic<PolygonImpl>;
using FillerSelection = selections::_FillerSelection<PolygonImpl>;
using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>;
using DJDHeuristic = selections::_DJDHeuristic<PolygonImpl>;
using NfpPlacer = strategies::_NofitPolyPlacer<PolygonImpl>;
using BottomLeftPlacer = strategies::_BottomLeftPlacer<PolygonImpl>;
using NfpPlacer = placers::_NofitPolyPlacer<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),
__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),
__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 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 {

View File

@ -5,7 +5,7 @@
#include "placer_boilerplate.hpp"
namespace libnest2d { namespace strategies {
namespace libnest2d { namespace placers {
template<class T, class = T> struct Epsilon {};

View File

@ -21,6 +21,12 @@
#include "tools/svgtools.hpp"
#ifdef USE_TBB
#include <tbb/parallel_for.h>
#elif defined(_OPENMP)
#include <omp.h>
#endif
namespace libnest2d {
namespace __parallel {
@ -33,20 +39,52 @@ using TIteratorValue = typename iterator_traits<It>::value_type;
template<class Iterator>
inline void enumerate(
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)
{
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);
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<Key, nfp::NfpResult<S>>;
}
namespace strategies {
namespace placers {
template<class RawShape>
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<double(const nfp::Shapes<RawShape>&, const _Item<RawShape>&,
const ItemGroup&)>
object_function;
std::function<double(const _Item<RawShape>&)> 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<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}),
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<opt::Method::L_SUBPLEX> 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<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;
// }
__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<NfpLevel::CONVEX_ONLY>(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<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>;
template<class Range = ConstItemRange<typename Base::DefaultIter>>
@ -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<double>;
using OptResults = std::vector<OptResult>;
@ -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<double>(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<double>(pos),

View File

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

View File

@ -8,7 +8,7 @@
#include "selection_boilerplate.hpp"
namespace libnest2d { namespace strategies {
namespace libnest2d { namespace selections {
/**
* Selection heuristic based on [López-Camacho]\

View File

@ -3,7 +3,7 @@
#include "selection_boilerplate.hpp"
namespace libnest2d { namespace strategies {
namespace libnest2d { namespace selections {
template<class RawShape>
class _FillerSelection: public SelectionBoilerplate<RawShape> {

View File

@ -4,7 +4,7 @@
#include "../libnest2d.hpp"
#include "selection_boilerplate.hpp"
namespace libnest2d { namespace strategies {
namespace libnest2d { namespace selections {
template<class RawShape>
class _FirstFitSelection: public SelectionBoilerplate<RawShape> {

View File

@ -3,8 +3,7 @@
#include "../libnest2d.hpp"
namespace libnest2d {
namespace strategies {
namespace libnest2d { namespace selections {
template<class RawShape>
class SelectionBoilerplate {

View File

@ -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<PointImpl> c = boundingCircle<PolygonImpl>(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<PolygonImpl>(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<ItemPair>& testdata) {
auto& exportfun = exportSVG<SCALE, Box>;
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<ItemPair>& testdata) {
auto&& nfp = nfp::noFitPolygon<lvl>(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<std::reference_wrapper<Item>> 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<ItemPair>& testdata) {
bool touching = Item::touches(tmp, stationary);
if(!touching) {
if(!touching || !valid.first) {
std::vector<std::reference_wrapper<Item>> inp = {
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) {
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<PolygonImpl> ecache(input);
placers::EdgeCache<PolygonImpl> ecache(input);
auto first = *input.begin();
ASSERT_TRUE(getX(first) == getX(ecache.coords(0)));

View File

@ -32,15 +32,18 @@ template<class S> 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<Coord> il):
x(*il.begin()), y(*std::next(il.begin())) {}
};
@ -58,8 +61,10 @@ template<class S> 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<class S> 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<Vector>::const_iterator cbegin() const { return v_.cbegin(); }
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]; }
const Vector& operator[](size_t idx) const { return v_[idx]; }
template<class...Args>
@ -83,12 +90,16 @@ template<class S> 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<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)) &&
(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<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);
// 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<class S> struct _alg {
d = dNAN;
}
}
if(isnan(d)){
if(!isnan(d)){
distances.emplace_back(d);
}
}
@ -392,7 +403,7 @@ template<class S> struct _alg {
auto d = pointDistance(E,A,B,direction);
if(!isnan(d) && _almostEqual(d, 0))
{ // 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)){
d = dNAN;
}
@ -407,7 +418,7 @@ template<class S> struct _alg {
if(!isnan(d) && _almostEqual(d, 0))
{ // && crossE<crossF A currently touches 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)){
d = dNAN;
}
@ -424,17 +435,29 @@ template<class S> 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<class S> 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<class S> 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<class S> 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<class S> 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<class S> struct _alg {
// maintain a list of touching points/edges
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;
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
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<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
std::vector<V> vectors;
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
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<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;
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>
//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;
using alg = __svgnest::_alg<PolygonImpl>;
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)};
}
};

View File

@ -100,55 +100,54 @@ namespace bgi = boost::geometry::index;
using SpatElement = std::pair<Box, unsigned>;
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
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*/>
objfunc(const PointImpl& bincenter,
double bin_area,
sl::Shapes<PolygonImpl>& pile, // The currently arranged pile
const shapelike::Shapes<PolygonImpl>& merged_pile,
const Box& pilebb,
const ItemGroup& items,
const Item &item,
double bin_area,
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
SpatIndex& spatindex,
const SpatIndex& spatindex,
const ItemGroup& remaining
)
{
using Coord = TCoord<PointImpl>;
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<PolygonImpl> ec(chull);
auto mp = merged_pile;
mp.emplace_back(item.transformedShape());
auto chull = sl::convexHull(mp);
placers::EdgeCache<PolygonImpl> 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<SpatElement> 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<PolygonImpl>{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<class TBin>
@ -280,7 +279,8 @@ class AutoArranger {};
template<class TBin>
class _ArrBase {
protected:
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>;
using Placer = TPacker<TBin>;
using Selector = FirstFitSelection;
using Packer = Nester<Placer, Selector>;
using PConfig = typename Packer::PlacementConfig;
@ -290,10 +290,12 @@ protected:
Packer pck_;
PConfig pconf_; // Placement configuration
double bin_area_;
std::vector<double> 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<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
areacache_.clear();
rtree_.clear();
return pck_.executeIndexed(std::forward<Args>(args)...);
}
@ -320,26 +346,28 @@ public:
std::function<void(unsigned)> 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_;
// 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<void(unsigned)> progressind):
_ArrBase<lnCircle>(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<void(unsigned)> progressind):
_ArrBase<PolygonImpl>(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<void(unsigned)> progressind):
_ArrBase<Box>(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);
};