Parallel placer now works with the custom Slic3r object function. Works an order of magnitude faster.
This commit is contained in:
parent
8617b0a409
commit
e522ad1a00
@ -729,6 +729,7 @@ add_custom_target(pot
|
||||
set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d")
|
||||
|
||||
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})
|
||||
|
||||
|
@ -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})
|
||||
|
322
xs/src/libnest2d/cmake_modules/FindTBB.cmake
Normal file
322
xs/src/libnest2d/cmake_modules/FindTBB.cmake
Normal file
@ -0,0 +1,322 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2015 Justus Calvin
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
#
|
||||
# FindTBB
|
||||
# -------
|
||||
#
|
||||
# Find TBB include directories and libraries.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# find_package(TBB [major[.minor]] [EXACT]
|
||||
# [QUIET] [REQUIRED]
|
||||
# [[COMPONENTS] [components...]]
|
||||
# [OPTIONAL_COMPONENTS components...])
|
||||
#
|
||||
# where the allowed components are tbbmalloc and tbb_preview. Users may modify
|
||||
# the behavior of this module with the following variables:
|
||||
#
|
||||
# * TBB_ROOT_DIR - The base directory the of TBB installation.
|
||||
# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files.
|
||||
# * TBB_LIBRARY - The directory that contains the TBB library files.
|
||||
# * TBB_<library>_LIBRARY - The path of the TBB the corresponding TBB library.
|
||||
# These libraries, if specified, override the
|
||||
# corresponding library search results, where <library>
|
||||
# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug,
|
||||
# tbb_preview, or tbb_preview_debug.
|
||||
# * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will
|
||||
# be used instead of the release version.
|
||||
# * TBB_STATIC - Static linking of libraries with a _static suffix.
|
||||
# For example, on Windows a tbb_static.lib will be searched for
|
||||
# instead of tbb.lib.
|
||||
#
|
||||
# Users may modify the behavior of this module with the following environment
|
||||
# variables:
|
||||
#
|
||||
# * TBB_INSTALL_DIR
|
||||
# * TBBROOT
|
||||
# * LIBRARY_PATH
|
||||
#
|
||||
# This module will set the following variables:
|
||||
#
|
||||
# * TBB_FOUND - Set to false, or undefined, if we haven’t found, or
|
||||
# don’t want to use TBB.
|
||||
# * TBB_<component>_FOUND - If False, optional <component> part of TBB sytem is
|
||||
# not available.
|
||||
# * TBB_VERSION - The full version string
|
||||
# * TBB_VERSION_MAJOR - The major version
|
||||
# * TBB_VERSION_MINOR - The minor version
|
||||
# * TBB_INTERFACE_VERSION - The interface version number defined in
|
||||
# tbb/tbb_stddef.h.
|
||||
# * TBB_<library>_LIBRARY_RELEASE - The path of the TBB release version of
|
||||
# <library>, where <library> may be tbb, tbb_debug,
|
||||
# tbbmalloc, tbbmalloc_debug, tbb_preview, or
|
||||
# tbb_preview_debug.
|
||||
# * TBB_<library>_LIBRARY_DEGUG - The path of the TBB release version of
|
||||
# <library>, where <library> may be tbb, tbb_debug,
|
||||
# tbbmalloc, tbbmalloc_debug, tbb_preview, or
|
||||
# tbb_preview_debug.
|
||||
#
|
||||
# The following varibles should be used to build and link with TBB:
|
||||
#
|
||||
# * TBB_INCLUDE_DIRS - The include directory for TBB.
|
||||
# * TBB_LIBRARIES - The libraries to link against to use TBB.
|
||||
# * TBB_LIBRARIES_RELEASE - The release libraries to link against to use TBB.
|
||||
# * TBB_LIBRARIES_DEBUG - The debug libraries to link against to use TBB.
|
||||
# * TBB_DEFINITIONS - Definitions to use when compiling code that uses
|
||||
# TBB.
|
||||
# * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that
|
||||
# uses TBB.
|
||||
# * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that
|
||||
# uses TBB.
|
||||
#
|
||||
# This module will also create the "tbb" target that may be used when building
|
||||
# executables and libraries.
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
if(NOT TBB_FOUND)
|
||||
|
||||
##################################
|
||||
# Check the build type
|
||||
##################################
|
||||
|
||||
if(NOT DEFINED TBB_USE_DEBUG_BUILD)
|
||||
if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)")
|
||||
set(TBB_BUILD_TYPE DEBUG)
|
||||
else()
|
||||
set(TBB_BUILD_TYPE RELEASE)
|
||||
endif()
|
||||
elseif(TBB_USE_DEBUG_BUILD)
|
||||
set(TBB_BUILD_TYPE DEBUG)
|
||||
else()
|
||||
set(TBB_BUILD_TYPE RELEASE)
|
||||
endif()
|
||||
|
||||
##################################
|
||||
# Set the TBB search directories
|
||||
##################################
|
||||
|
||||
# Define search paths based on user input and environment variables
|
||||
set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT})
|
||||
|
||||
# Define the search directories based on the current platform
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB"
|
||||
"C:/Program Files (x86)/Intel/TBB")
|
||||
|
||||
# Set the target architecture
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(TBB_ARCHITECTURE "intel64")
|
||||
else()
|
||||
set(TBB_ARCHITECTURE "ia32")
|
||||
endif()
|
||||
|
||||
# Set the TBB search library path search suffix based on the version of VC
|
||||
if(WINDOWS_STORE)
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui")
|
||||
elseif(MSVC14)
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14")
|
||||
elseif(MSVC12)
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12")
|
||||
elseif(MSVC11)
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11")
|
||||
elseif(MSVC10)
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10")
|
||||
endif()
|
||||
|
||||
# Add the library path search suffix for the VC independent version of TBB
|
||||
list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt")
|
||||
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
# OS X
|
||||
set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
|
||||
|
||||
# TODO: Check to see which C++ library is being used by the compiler.
|
||||
if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0)
|
||||
# The default C++ library on OS X 10.9 and later is libc++
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib")
|
||||
else()
|
||||
set(TBB_LIB_PATH_SUFFIX "lib")
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# Linux
|
||||
set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
|
||||
|
||||
# TODO: Check compiler version to see the suffix should be <arch>/gcc4.1 or
|
||||
# <arch>/gcc4.1. For now, assume that the compiler is more recent than
|
||||
# gcc 4.4.x or later.
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
|
||||
set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
##################################
|
||||
# Find the TBB include dir
|
||||
##################################
|
||||
|
||||
find_path(TBB_INCLUDE_DIRS tbb/tbb.h
|
||||
HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR}
|
||||
PATHS ${TBB_DEFAULT_SEARCH_DIR}
|
||||
PATH_SUFFIXES include)
|
||||
|
||||
##################################
|
||||
# Set version strings
|
||||
##################################
|
||||
|
||||
if(TBB_INCLUDE_DIRS)
|
||||
file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file)
|
||||
string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1"
|
||||
TBB_VERSION_MAJOR "${_tbb_version_file}")
|
||||
string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1"
|
||||
TBB_VERSION_MINOR "${_tbb_version_file}")
|
||||
string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1"
|
||||
TBB_INTERFACE_VERSION "${_tbb_version_file}")
|
||||
set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}")
|
||||
endif()
|
||||
|
||||
##################################
|
||||
# Find TBB components
|
||||
##################################
|
||||
|
||||
if(TBB_VERSION VERSION_LESS 4.3)
|
||||
set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb)
|
||||
else()
|
||||
set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb)
|
||||
endif()
|
||||
|
||||
if(TBB_STATIC)
|
||||
set(TBB_STATIC_SUFFIX "_static")
|
||||
endif()
|
||||
|
||||
# Find each component
|
||||
foreach(_comp ${TBB_SEARCH_COMPOMPONENTS})
|
||||
if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};")
|
||||
|
||||
# Search for the libraries
|
||||
find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX}
|
||||
HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
|
||||
PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
|
||||
PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
|
||||
|
||||
find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug
|
||||
HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
|
||||
PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
|
||||
PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
|
||||
|
||||
if(TBB_${_comp}_LIBRARY_DEBUG)
|
||||
list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
if(TBB_${_comp}_LIBRARY_RELEASE)
|
||||
list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY)
|
||||
set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}")
|
||||
endif()
|
||||
|
||||
if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}")
|
||||
set(TBB_${_comp}_FOUND TRUE)
|
||||
else()
|
||||
set(TBB_${_comp}_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
# Mark internal variables as advanced
|
||||
mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE)
|
||||
mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG)
|
||||
mark_as_advanced(TBB_${_comp}_LIBRARY)
|
||||
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
unset(TBB_STATIC_SUFFIX)
|
||||
|
||||
##################################
|
||||
# Set compile flags and libraries
|
||||
##################################
|
||||
|
||||
set(TBB_DEFINITIONS_RELEASE "")
|
||||
set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1")
|
||||
|
||||
if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
|
||||
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}")
|
||||
set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
|
||||
elseif(TBB_LIBRARIES_RELEASE)
|
||||
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}")
|
||||
set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}")
|
||||
elseif(TBB_LIBRARIES_DEBUG)
|
||||
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}")
|
||||
set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}")
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(TBB
|
||||
REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES
|
||||
HANDLE_COMPONENTS
|
||||
VERSION_VAR TBB_VERSION)
|
||||
|
||||
##################################
|
||||
# Create targets
|
||||
##################################
|
||||
|
||||
if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
|
||||
add_library(tbb SHARED IMPORTED)
|
||||
set_target_properties(tbb PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
|
||||
IMPORTED_LOCATION ${TBB_LIBRARIES})
|
||||
if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG)
|
||||
set_target_properties(tbb PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:TBB_USE_DEBUG=1>"
|
||||
IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG}
|
||||
IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG}
|
||||
IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE}
|
||||
IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE}
|
||||
)
|
||||
elseif(TBB_LIBRARIES_RELEASE)
|
||||
set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE})
|
||||
else()
|
||||
set_target_properties(tbb PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}"
|
||||
IMPORTED_LOCATION ${TBB_LIBRARIES_DEBUG}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES)
|
||||
|
||||
unset(TBB_ARCHITECTURE)
|
||||
unset(TBB_BUILD_TYPE)
|
||||
unset(TBB_LIB_PATH_SUFFIX)
|
||||
unset(TBB_DEFAULT_SEARCH_DIR)
|
||||
|
||||
if(TBB_DEBUG)
|
||||
message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}")
|
||||
message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}")
|
||||
message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}")
|
||||
message(STATUS " TBB_DEFINITIONS_DEBUG = ${TBB_DEFINITIONS_DEBUG}")
|
||||
message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}")
|
||||
message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}")
|
||||
message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}")
|
||||
endif()
|
||||
|
||||
endif()
|
@ -54,7 +54,7 @@ void arrangeRectangles() {
|
||||
|
||||
const int SCALE = 1000000;
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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>;
|
||||
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 {
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include "placer_boilerplate.hpp"
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
namespace libnest2d { namespace placers {
|
||||
|
||||
template<class T, class = T> struct Epsilon {};
|
||||
|
||||
|
@ -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),
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "../libnest2d.hpp"
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
namespace libnest2d { namespace placers {
|
||||
|
||||
struct EmptyConfig {};
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "selection_boilerplate.hpp"
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
namespace libnest2d { namespace selections {
|
||||
|
||||
/**
|
||||
* Selection heuristic based on [López-Camacho]\
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "selection_boilerplate.hpp"
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
namespace libnest2d { namespace selections {
|
||||
|
||||
template<class RawShape>
|
||||
class _FillerSelection: public SelectionBoilerplate<RawShape> {
|
||||
|
@ -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> {
|
||||
|
@ -3,8 +3,7 @@
|
||||
|
||||
#include "../libnest2d.hpp"
|
||||
|
||||
namespace libnest2d {
|
||||
namespace strategies {
|
||||
namespace libnest2d { namespace selections {
|
||||
|
||||
template<class RawShape>
|
||||
class SelectionBoilerplate {
|
||||
|
@ -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)));
|
||||
|
@ -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()};
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)};
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user