Merge remote-tracking branch 'origin/feature_arrange_with_libnest2d' into dev
# Conflicts: # xs/src/slic3r/AppController.cpp
This commit is contained in:
commit
6e2ed48e5a
@ -813,6 +813,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})
|
||||
|
||||
|
@ -31,6 +31,7 @@ set(LIBNEST2D_SRCFILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/metaloop.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/rotfinder.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/placer_boilerplate.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp
|
||||
@ -89,14 +90,39 @@ if(LIBNEST2D_UNITTESTS)
|
||||
endif()
|
||||
|
||||
if(LIBNEST2D_BUILD_EXAMPLES)
|
||||
|
||||
add_executable(example examples/main.cpp
|
||||
# tools/libnfpglue.hpp
|
||||
# tools/libnfpglue.cpp
|
||||
tools/nfp_svgnest.hpp
|
||||
tools/nfp_svgnest_glue.hpp
|
||||
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})
|
||||
|
@ -9,18 +9,28 @@ with templated geometry types. These geometries can have custom or already
|
||||
existing implementation to avoid copying or having unnecessary dependencies.
|
||||
|
||||
A default backend is provided if the user of the library just wants to use it
|
||||
out of the box without additional integration. The default backend is reasonably
|
||||
out of the box without additional integration. This backend is reasonably
|
||||
fast and robust, being built on top of boost geometry and the
|
||||
[polyclipping](http://www.angusj.com/delphi/clipper.php) library. Usage of
|
||||
this default backend implies the dependency on these packages as well as the
|
||||
compilation of the backend itself (The default backend is not yet header only).
|
||||
this default backend implies the dependency on these packages but its header
|
||||
only as well.
|
||||
|
||||
This software is currently under construction and lacks a throughout
|
||||
documentation and some essential algorithms as well. At this stage it works well
|
||||
for rectangles and convex closed polygons without considering holes and
|
||||
concavities.
|
||||
|
||||
Holes and non-convex polygons will be usable in the near future as well.
|
||||
Holes and non-convex polygons will be usable in the near future as well. The
|
||||
no fit polygon based placer module combined with the first fit selection
|
||||
strategy is now used in the [Slic3r](https://github.com/prusa3d/Slic3r)
|
||||
application's arrangement feature. It uses local optimization techniques to find
|
||||
the best placement of each new item based on some features of the arrangement.
|
||||
|
||||
In the near future I would like to use machine learning to evaluate the
|
||||
placements and (or) the order if items in which they are placed and see what
|
||||
results can be obtained. This is a different approach than that of SVGnest which
|
||||
uses genetic algorithms to find better and better selection orders. Maybe the
|
||||
two approaches can be combined as well.
|
||||
|
||||
# References
|
||||
- [SVGNest](https://github.com/Jack000/SVGnest)
|
||||
|
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()
|
@ -1,7 +1,6 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
//#define DEBUG_EXPORT_NFP
|
||||
|
||||
#include <libnest2d.h>
|
||||
@ -9,7 +8,11 @@
|
||||
#include "tests/printer_parts.h"
|
||||
#include "tools/benchmark.h"
|
||||
#include "tools/svgtools.hpp"
|
||||
#include "libnest2d/rotfinder.hpp"
|
||||
|
||||
//#include "tools/libnfpglue.hpp"
|
||||
//#include "tools/nfp_svgnest_glue.hpp"
|
||||
|
||||
|
||||
using namespace libnest2d;
|
||||
using ItemGroup = std::vector<std::reference_wrapper<Item>>;
|
||||
@ -50,499 +53,57 @@ void arrangeRectangles() {
|
||||
using namespace libnest2d;
|
||||
|
||||
const int SCALE = 1000000;
|
||||
// const int SCALE = 1;
|
||||
std::vector<Rectangle> rects = {
|
||||
{80*SCALE, 80*SCALE},
|
||||
{60*SCALE, 90*SCALE},
|
||||
{70*SCALE, 30*SCALE},
|
||||
{80*SCALE, 60*SCALE},
|
||||
{60*SCALE, 60*SCALE},
|
||||
{60*SCALE, 40*SCALE},
|
||||
{40*SCALE, 40*SCALE},
|
||||
{10*SCALE, 10*SCALE},
|
||||
{10*SCALE, 10*SCALE},
|
||||
{10*SCALE, 10*SCALE},
|
||||
{10*SCALE, 10*SCALE},
|
||||
{10*SCALE, 10*SCALE},
|
||||
{5*SCALE, 5*SCALE},
|
||||
{5*SCALE, 5*SCALE},
|
||||
{5*SCALE, 5*SCALE},
|
||||
{5*SCALE, 5*SCALE},
|
||||
{5*SCALE, 5*SCALE},
|
||||
{5*SCALE, 5*SCALE},
|
||||
{5*SCALE, 5*SCALE},
|
||||
{20*SCALE, 20*SCALE}
|
||||
};
|
||||
|
||||
// std::vector<Rectangle> rects = {
|
||||
// {20*SCALE, 10*SCALE},
|
||||
// {20*SCALE, 10*SCALE},
|
||||
// {20*SCALE, 20*SCALE},
|
||||
// };
|
||||
std::vector<Item> rects(202, {
|
||||
{-9945219, -3065619},
|
||||
{-9781479, -2031780},
|
||||
{-9510560, -1020730},
|
||||
{-9135450, -43529},
|
||||
{-2099999, 14110899},
|
||||
{2099999, 14110899},
|
||||
{9135450, -43529},
|
||||
{9510560, -1020730},
|
||||
{9781479, -2031780},
|
||||
{9945219, -3065619},
|
||||
{10000000, -4110899},
|
||||
{9945219, -5156179},
|
||||
{9781479, -6190019},
|
||||
{9510560, -7201069},
|
||||
{9135450, -8178270},
|
||||
{8660249, -9110899},
|
||||
{8090169, -9988750},
|
||||
{7431449, -10802209},
|
||||
{6691309, -11542349},
|
||||
{5877850, -12201069},
|
||||
{5000000, -12771149},
|
||||
{4067369, -13246350},
|
||||
{3090169, -13621459},
|
||||
{2079119, -13892379},
|
||||
{1045279, -14056119},
|
||||
{0, -14110899},
|
||||
{-1045279, -14056119},
|
||||
{-2079119, -13892379},
|
||||
{-3090169, -13621459},
|
||||
{-4067369, -13246350},
|
||||
{-5000000, -12771149},
|
||||
{-5877850, -12201069},
|
||||
{-6691309, -11542349},
|
||||
{-7431449, -10802209},
|
||||
{-8090169, -9988750},
|
||||
{-8660249, -9110899},
|
||||
{-9135450, -8178270},
|
||||
{-9510560, -7201069},
|
||||
{-9781479, -6190019},
|
||||
{-9945219, -5156179},
|
||||
{-10000000, -4110899},
|
||||
{-9945219, -3065619},
|
||||
});
|
||||
|
||||
// std::vector<Item> input {
|
||||
// {{0, 0}, {0, 20*SCALE}, {10*SCALE, 0}, {0, 0}}
|
||||
// };
|
||||
|
||||
std::vector<Item> crasher =
|
||||
{
|
||||
{
|
||||
{-5000000, 8954050},
|
||||
{5000000, 8954050},
|
||||
{5000000, -45949},
|
||||
{4972609, -568549},
|
||||
{3500000, -8954050},
|
||||
{-3500000, -8954050},
|
||||
{-4972609, -568549},
|
||||
{-5000000, -45949},
|
||||
{-5000000, 8954050},
|
||||
},
|
||||
{
|
||||
{-5000000, 8954050},
|
||||
{5000000, 8954050},
|
||||
{5000000, -45949},
|
||||
{4972609, -568549},
|
||||
{3500000, -8954050},
|
||||
{-3500000, -8954050},
|
||||
{-4972609, -568549},
|
||||
{-5000000, -45949},
|
||||
{-5000000, 8954050},
|
||||
},
|
||||
{
|
||||
{-5000000, 8954050},
|
||||
{5000000, 8954050},
|
||||
{5000000, -45949},
|
||||
{4972609, -568549},
|
||||
{3500000, -8954050},
|
||||
{-3500000, -8954050},
|
||||
{-4972609, -568549},
|
||||
{-5000000, -45949},
|
||||
{-5000000, 8954050},
|
||||
},
|
||||
{
|
||||
{-5000000, 8954050},
|
||||
{5000000, 8954050},
|
||||
{5000000, -45949},
|
||||
{4972609, -568549},
|
||||
{3500000, -8954050},
|
||||
{-3500000, -8954050},
|
||||
{-4972609, -568549},
|
||||
{-5000000, -45949},
|
||||
{-5000000, 8954050},
|
||||
},
|
||||
{
|
||||
{-5000000, 8954050},
|
||||
{5000000, 8954050},
|
||||
{5000000, -45949},
|
||||
{4972609, -568549},
|
||||
{3500000, -8954050},
|
||||
{-3500000, -8954050},
|
||||
{-4972609, -568549},
|
||||
{-5000000, -45949},
|
||||
{-5000000, 8954050},
|
||||
},
|
||||
{
|
||||
{-5000000, 8954050},
|
||||
{5000000, 8954050},
|
||||
{5000000, -45949},
|
||||
{4972609, -568549},
|
||||
{3500000, -8954050},
|
||||
{-3500000, -8954050},
|
||||
{-4972609, -568549},
|
||||
{-5000000, -45949},
|
||||
{-5000000, 8954050},
|
||||
},
|
||||
{
|
||||
{-9945219, -3065619},
|
||||
{-9781479, -2031780},
|
||||
{-9510560, -1020730},
|
||||
{-9135450, -43529},
|
||||
{-2099999, 14110899},
|
||||
{2099999, 14110899},
|
||||
{9135450, -43529},
|
||||
{9510560, -1020730},
|
||||
{9781479, -2031780},
|
||||
{9945219, -3065619},
|
||||
{10000000, -4110899},
|
||||
{9945219, -5156179},
|
||||
{9781479, -6190020},
|
||||
{9510560, -7201069},
|
||||
{9135450, -8178270},
|
||||
{8660249, -9110899},
|
||||
{8090169, -9988750},
|
||||
{7431449, -10802200},
|
||||
{6691309, -11542300},
|
||||
{5877850, -12201100},
|
||||
{5000000, -12771100},
|
||||
{4067369, -13246399},
|
||||
{3090169, -13621500},
|
||||
{2079119, -13892399},
|
||||
{1045279, -14056099},
|
||||
{0, -14110899},
|
||||
{-1045279, -14056099},
|
||||
{-2079119, -13892399},
|
||||
{-3090169, -13621500},
|
||||
{-4067369, -13246399},
|
||||
{-5000000, -12771100},
|
||||
{-5877850, -12201100},
|
||||
{-6691309, -11542300},
|
||||
{-7431449, -10802200},
|
||||
{-8090169, -9988750},
|
||||
{-8660249, -9110899},
|
||||
{-9135450, -8178270},
|
||||
{-9510560, -7201069},
|
||||
{-9781479, -6190020},
|
||||
{-9945219, -5156179},
|
||||
{-10000000, -4110899},
|
||||
{-9945219, -3065619},
|
||||
},
|
||||
{
|
||||
{-9945219, -3065619},
|
||||
{-9781479, -2031780},
|
||||
{-9510560, -1020730},
|
||||
{-9135450, -43529},
|
||||
{-2099999, 14110899},
|
||||
{2099999, 14110899},
|
||||
{9135450, -43529},
|
||||
{9510560, -1020730},
|
||||
{9781479, -2031780},
|
||||
{9945219, -3065619},
|
||||
{10000000, -4110899},
|
||||
{9945219, -5156179},
|
||||
{9781479, -6190020},
|
||||
{9510560, -7201069},
|
||||
{9135450, -8178270},
|
||||
{8660249, -9110899},
|
||||
{8090169, -9988750},
|
||||
{7431449, -10802200},
|
||||
{6691309, -11542300},
|
||||
{5877850, -12201100},
|
||||
{5000000, -12771100},
|
||||
{4067369, -13246399},
|
||||
{3090169, -13621500},
|
||||
{2079119, -13892399},
|
||||
{1045279, -14056099},
|
||||
{0, -14110899},
|
||||
{-1045279, -14056099},
|
||||
{-2079119, -13892399},
|
||||
{-3090169, -13621500},
|
||||
{-4067369, -13246399},
|
||||
{-5000000, -12771100},
|
||||
{-5877850, -12201100},
|
||||
{-6691309, -11542300},
|
||||
{-7431449, -10802200},
|
||||
{-8090169, -9988750},
|
||||
{-8660249, -9110899},
|
||||
{-9135450, -8178270},
|
||||
{-9510560, -7201069},
|
||||
{-9781479, -6190020},
|
||||
{-9945219, -5156179},
|
||||
{-10000000, -4110899},
|
||||
{-9945219, -3065619},
|
||||
},
|
||||
{
|
||||
{-9945219, -3065619},
|
||||
{-9781479, -2031780},
|
||||
{-9510560, -1020730},
|
||||
{-9135450, -43529},
|
||||
{-2099999, 14110899},
|
||||
{2099999, 14110899},
|
||||
{9135450, -43529},
|
||||
{9510560, -1020730},
|
||||
{9781479, -2031780},
|
||||
{9945219, -3065619},
|
||||
{10000000, -4110899},
|
||||
{9945219, -5156179},
|
||||
{9781479, -6190020},
|
||||
{9510560, -7201069},
|
||||
{9135450, -8178270},
|
||||
{8660249, -9110899},
|
||||
{8090169, -9988750},
|
||||
{7431449, -10802200},
|
||||
{6691309, -11542300},
|
||||
{5877850, -12201100},
|
||||
{5000000, -12771100},
|
||||
{4067369, -13246399},
|
||||
{3090169, -13621500},
|
||||
{2079119, -13892399},
|
||||
{1045279, -14056099},
|
||||
{0, -14110899},
|
||||
{-1045279, -14056099},
|
||||
{-2079119, -13892399},
|
||||
{-3090169, -13621500},
|
||||
{-4067369, -13246399},
|
||||
{-5000000, -12771100},
|
||||
{-5877850, -12201100},
|
||||
{-6691309, -11542300},
|
||||
{-7431449, -10802200},
|
||||
{-8090169, -9988750},
|
||||
{-8660249, -9110899},
|
||||
{-9135450, -8178270},
|
||||
{-9510560, -7201069},
|
||||
{-9781479, -6190020},
|
||||
{-9945219, -5156179},
|
||||
{-10000000, -4110899},
|
||||
{-9945219, -3065619},
|
||||
},
|
||||
{
|
||||
{-9945219, -3065619},
|
||||
{-9781479, -2031780},
|
||||
{-9510560, -1020730},
|
||||
{-9135450, -43529},
|
||||
{-2099999, 14110899},
|
||||
{2099999, 14110899},
|
||||
{9135450, -43529},
|
||||
{9510560, -1020730},
|
||||
{9781479, -2031780},
|
||||
{9945219, -3065619},
|
||||
{10000000, -4110899},
|
||||
{9945219, -5156179},
|
||||
{9781479, -6190020},
|
||||
{9510560, -7201069},
|
||||
{9135450, -8178270},
|
||||
{8660249, -9110899},
|
||||
{8090169, -9988750},
|
||||
{7431449, -10802200},
|
||||
{6691309, -11542300},
|
||||
{5877850, -12201100},
|
||||
{5000000, -12771100},
|
||||
{4067369, -13246399},
|
||||
{3090169, -13621500},
|
||||
{2079119, -13892399},
|
||||
{1045279, -14056099},
|
||||
{0, -14110899},
|
||||
{-1045279, -14056099},
|
||||
{-2079119, -13892399},
|
||||
{-3090169, -13621500},
|
||||
{-4067369, -13246399},
|
||||
{-5000000, -12771100},
|
||||
{-5877850, -12201100},
|
||||
{-6691309, -11542300},
|
||||
{-7431449, -10802200},
|
||||
{-8090169, -9988750},
|
||||
{-8660249, -9110899},
|
||||
{-9135450, -8178270},
|
||||
{-9510560, -7201069},
|
||||
{-9781479, -6190020},
|
||||
{-9945219, -5156179},
|
||||
{-10000000, -4110899},
|
||||
{-9945219, -3065619},
|
||||
},
|
||||
{
|
||||
{-9945219, -3065619},
|
||||
{-9781479, -2031780},
|
||||
{-9510560, -1020730},
|
||||
{-9135450, -43529},
|
||||
{-2099999, 14110899},
|
||||
{2099999, 14110899},
|
||||
{9135450, -43529},
|
||||
{9510560, -1020730},
|
||||
{9781479, -2031780},
|
||||
{9945219, -3065619},
|
||||
{10000000, -4110899},
|
||||
{9945219, -5156179},
|
||||
{9781479, -6190020},
|
||||
{9510560, -7201069},
|
||||
{9135450, -8178270},
|
||||
{8660249, -9110899},
|
||||
{8090169, -9988750},
|
||||
{7431449, -10802200},
|
||||
{6691309, -11542300},
|
||||
{5877850, -12201100},
|
||||
{5000000, -12771100},
|
||||
{4067369, -13246399},
|
||||
{3090169, -13621500},
|
||||
{2079119, -13892399},
|
||||
{1045279, -14056099},
|
||||
{0, -14110899},
|
||||
{-1045279, -14056099},
|
||||
{-2079119, -13892399},
|
||||
{-3090169, -13621500},
|
||||
{-4067369, -13246399},
|
||||
{-5000000, -12771100},
|
||||
{-5877850, -12201100},
|
||||
{-6691309, -11542300},
|
||||
{-7431449, -10802200},
|
||||
{-8090169, -9988750},
|
||||
{-8660249, -9110899},
|
||||
{-9135450, -8178270},
|
||||
{-9510560, -7201069},
|
||||
{-9781479, -6190020},
|
||||
{-9945219, -5156179},
|
||||
{-10000000, -4110899},
|
||||
{-9945219, -3065619},
|
||||
},
|
||||
{
|
||||
{-9945219, -3065619},
|
||||
{-9781479, -2031780},
|
||||
{-9510560, -1020730},
|
||||
{-9135450, -43529},
|
||||
{-2099999, 14110899},
|
||||
{2099999, 14110899},
|
||||
{9135450, -43529},
|
||||
{9510560, -1020730},
|
||||
{9781479, -2031780},
|
||||
{9945219, -3065619},
|
||||
{10000000, -4110899},
|
||||
{9945219, -5156179},
|
||||
{9781479, -6190020},
|
||||
{9510560, -7201069},
|
||||
{9135450, -8178270},
|
||||
{8660249, -9110899},
|
||||
{8090169, -9988750},
|
||||
{7431449, -10802200},
|
||||
{6691309, -11542300},
|
||||
{5877850, -12201100},
|
||||
{5000000, -12771100},
|
||||
{4067369, -13246399},
|
||||
{3090169, -13621500},
|
||||
{2079119, -13892399},
|
||||
{1045279, -14056099},
|
||||
{0, -14110899},
|
||||
{-1045279, -14056099},
|
||||
{-2079119, -13892399},
|
||||
{-3090169, -13621500},
|
||||
{-4067369, -13246399},
|
||||
{-5000000, -12771100},
|
||||
{-5877850, -12201100},
|
||||
{-6691309, -11542300},
|
||||
{-7431449, -10802200},
|
||||
{-8090169, -9988750},
|
||||
{-8660249, -9110899},
|
||||
{-9135450, -8178270},
|
||||
{-9510560, -7201069},
|
||||
{-9781479, -6190020},
|
||||
{-9945219, -5156179},
|
||||
{-10000000, -4110899},
|
||||
{-9945219, -3065619},
|
||||
},
|
||||
{
|
||||
{-9945219, -3065619},
|
||||
{-9781479, -2031780},
|
||||
{-9510560, -1020730},
|
||||
{-9135450, -43529},
|
||||
{-2099999, 14110899},
|
||||
{2099999, 14110899},
|
||||
{9135450, -43529},
|
||||
{9510560, -1020730},
|
||||
{9781479, -2031780},
|
||||
{9945219, -3065619},
|
||||
{10000000, -4110899},
|
||||
{9945219, -5156179},
|
||||
{9781479, -6190020},
|
||||
{9510560, -7201069},
|
||||
{9135450, -8178270},
|
||||
{8660249, -9110899},
|
||||
{8090169, -9988750},
|
||||
{7431449, -10802200},
|
||||
{6691309, -11542300},
|
||||
{5877850, -12201100},
|
||||
{5000000, -12771100},
|
||||
{4067369, -13246399},
|
||||
{3090169, -13621500},
|
||||
{2079119, -13892399},
|
||||
{1045279, -14056099},
|
||||
{0, -14110899},
|
||||
{-1045279, -14056099},
|
||||
{-2079119, -13892399},
|
||||
{-3090169, -13621500},
|
||||
{-4067369, -13246399},
|
||||
{-5000000, -12771100},
|
||||
{-5877850, -12201100},
|
||||
{-6691309, -11542300},
|
||||
{-7431449, -10802200},
|
||||
{-8090169, -9988750},
|
||||
{-8660249, -9110899},
|
||||
{-9135450, -8178270},
|
||||
{-9510560, -7201069},
|
||||
{-9781479, -6190020},
|
||||
{-9945219, -5156179},
|
||||
{-10000000, -4110899},
|
||||
{-9945219, -3065619},
|
||||
},
|
||||
{
|
||||
{-9945219, -3065619},
|
||||
{-9781479, -2031780},
|
||||
{-9510560, -1020730},
|
||||
{-9135450, -43529},
|
||||
{-2099999, 14110899},
|
||||
{2099999, 14110899},
|
||||
{9135450, -43529},
|
||||
{9510560, -1020730},
|
||||
{9781479, -2031780},
|
||||
{9945219, -3065619},
|
||||
{10000000, -4110899},
|
||||
{9945219, -5156179},
|
||||
{9781479, -6190020},
|
||||
{9510560, -7201069},
|
||||
{9135450, -8178270},
|
||||
{8660249, -9110899},
|
||||
{8090169, -9988750},
|
||||
{7431449, -10802200},
|
||||
{6691309, -11542300},
|
||||
{5877850, -12201100},
|
||||
{5000000, -12771100},
|
||||
{4067369, -13246399},
|
||||
{3090169, -13621500},
|
||||
{2079119, -13892399},
|
||||
{1045279, -14056099},
|
||||
{0, -14110899},
|
||||
{-1045279, -14056099},
|
||||
{-2079119, -13892399},
|
||||
{-3090169, -13621500},
|
||||
{-4067369, -13246399},
|
||||
{-5000000, -12771100},
|
||||
{-5877850, -12201100},
|
||||
{-6691309, -11542300},
|
||||
{-7431449, -10802200},
|
||||
{-8090169, -9988750},
|
||||
{-8660249, -9110899},
|
||||
{-9135450, -8178270},
|
||||
{-9510560, -7201069},
|
||||
{-9781479, -6190020},
|
||||
{-9945219, -5156179},
|
||||
{-10000000, -4110899},
|
||||
{-9945219, -3065619},
|
||||
},
|
||||
{
|
||||
{-18000000, -1000000},
|
||||
{-15000000, 22000000},
|
||||
{-11000000, 26000000},
|
||||
{11000000, 26000000},
|
||||
{15000000, 22000000},
|
||||
{18000000, -1000000},
|
||||
{18000000, -26000000},
|
||||
{-18000000, -26000000},
|
||||
{-18000000, -1000000},
|
||||
},
|
||||
};
|
||||
|
||||
std::vector<Item> proba = {
|
||||
{
|
||||
Rectangle(100, 2)
|
||||
},
|
||||
{
|
||||
Rectangle(100, 2)
|
||||
},
|
||||
{
|
||||
Rectangle(100, 2)
|
||||
},
|
||||
{
|
||||
Rectangle(10, 10)
|
||||
},
|
||||
};
|
||||
|
||||
proba[0].rotate(Pi/3);
|
||||
proba[1].rotate(Pi-Pi/3);
|
||||
|
||||
// std::vector<Item> input(25, Rectangle(70*SCALE, 10*SCALE));
|
||||
std::vector<Item> input;
|
||||
input.insert(input.end(), prusaParts().begin(), prusaParts().end());
|
||||
// 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 = {
|
||||
@ -560,10 +121,12 @@ void arrangeRectangles() {
|
||||
// {}
|
||||
// };
|
||||
|
||||
auto min_obj_distance = static_cast<Coord>(0*SCALE);
|
||||
// Circle bin({0, 0}, 125*SCALE);
|
||||
|
||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, Box>;
|
||||
using Packer = Arranger<Placer, FirstFitSelection>;
|
||||
auto min_obj_distance = static_cast<Coord>(6*SCALE);
|
||||
|
||||
using Placer = placers::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
|
||||
using Packer = Nester<Placer, FirstFitSelection>;
|
||||
|
||||
Packer arrange(bin, min_obj_distance);
|
||||
|
||||
@ -571,121 +134,23 @@ 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;
|
||||
|
||||
// auto bincenter = ShapeLike::boundingBox(bin).center();
|
||||
// pconf.object_function = [&bin, bincenter](
|
||||
// Placer::Pile pile, const Item& item,
|
||||
// double /*area*/, double norm, double penality) {
|
||||
|
||||
// using pl = PointLike;
|
||||
|
||||
// static const double BIG_ITEM_TRESHOLD = 0.2;
|
||||
// static const double GRAVITY_RATIO = 0.5;
|
||||
// static const double DENSITY_RATIO = 1.0 - GRAVITY_RATIO;
|
||||
|
||||
// // We will treat big items (compared to the print bed) differently
|
||||
// NfpPlacer::Pile bigs;
|
||||
// bigs.reserve(pile.size());
|
||||
// for(auto& p : pile) {
|
||||
// auto pbb = ShapeLike::boundingBox(p);
|
||||
// auto na = std::sqrt(pbb.width()*pbb.height())/norm;
|
||||
// if(na > BIG_ITEM_TRESHOLD) bigs.emplace_back(p);
|
||||
// }
|
||||
|
||||
// // Candidate item bounding box
|
||||
// auto ibb = item.boundingBox();
|
||||
|
||||
// // Calculate the full bounding box of the pile with the candidate item
|
||||
// pile.emplace_back(item.transformedShape());
|
||||
// auto fullbb = ShapeLike::boundingBox(pile);
|
||||
// pile.pop_back();
|
||||
|
||||
// // The bounding box of the big items (they will accumulate in the center
|
||||
// // of the pile
|
||||
// auto bigbb = bigs.empty()? fullbb : ShapeLike::boundingBox(bigs);
|
||||
|
||||
// // The size indicator of the candidate item. This is not the area,
|
||||
// // but almost...
|
||||
// auto itemnormarea = std::sqrt(ibb.width()*ibb.height())/norm;
|
||||
|
||||
// // Will hold the resulting score
|
||||
// double score = 0;
|
||||
|
||||
// if(itemnormarea > BIG_ITEM_TRESHOLD) {
|
||||
// // This branch is for the bigger items..
|
||||
// // Here we will use the closest point of the item bounding box to
|
||||
// // the already arranged pile. So not the bb center nor the a choosen
|
||||
// // corner but whichever is the closest to the center. This will
|
||||
// // prevent unwanted strange arrangements.
|
||||
|
||||
// auto minc = ibb.minCorner(); // bottom left corner
|
||||
// auto maxc = ibb.maxCorner(); // top right corner
|
||||
|
||||
// // top left and bottom right corners
|
||||
// auto top_left = PointImpl{getX(minc), getY(maxc)};
|
||||
// auto bottom_right = PointImpl{getX(maxc), getY(minc)};
|
||||
|
||||
// auto cc = fullbb.center(); // The gravity center
|
||||
|
||||
// // Now the distnce of the gravity center will be calculated to the
|
||||
// // five anchor points and the smallest will be chosen.
|
||||
// std::array<double, 5> dists;
|
||||
// dists[0] = pl::distance(minc, cc);
|
||||
// dists[1] = pl::distance(maxc, cc);
|
||||
// dists[2] = pl::distance(ibb.center(), cc);
|
||||
// dists[3] = pl::distance(top_left, cc);
|
||||
// dists[4] = pl::distance(bottom_right, cc);
|
||||
|
||||
// auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
|
||||
|
||||
// // Density is the pack density: how big is the arranged pile
|
||||
// auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
|
||||
// // The score is a weighted sum of the distance from pile center
|
||||
// // and the pile size
|
||||
// score = GRAVITY_RATIO * dist + DENSITY_RATIO * density;
|
||||
|
||||
// } else if(itemnormarea < BIG_ITEM_TRESHOLD && bigs.empty()) {
|
||||
// // If there are no big items, only small, we should consider the
|
||||
// // density here as well to not get silly results
|
||||
// auto bindist = pl::distance(ibb.center(), bincenter) / norm;
|
||||
// auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
// score = GRAVITY_RATIO * bindist + DENSITY_RATIO * density;
|
||||
// } else {
|
||||
// // Here there are the small items that should be placed around the
|
||||
// // already processed bigger items.
|
||||
// // No need to play around with the anchor points, the center will be
|
||||
// // just fine for small items
|
||||
// score = pl::distance(ibb.center(), bigbb.center()) / norm;
|
||||
// }
|
||||
|
||||
// // If it does not fit into the print bed we will beat it
|
||||
// // with a large penality. If we would not do this, there would be only
|
||||
// // one big pile that doesn't care whether it fits onto the print bed.
|
||||
// if(!NfpPlacer::wouldFit(fullbb, bin)) score = 2*penality - score;
|
||||
|
||||
// return score;
|
||||
// };
|
||||
pconf.accuracy = 0.65f;
|
||||
pconf.parallel = true;
|
||||
|
||||
Packer::SelectionConfig sconf;
|
||||
// sconf.allow_parallel = false;
|
||||
// sconf.force_parallel = false;
|
||||
// sconf.try_triplets = true;
|
||||
// sconf.try_reverse_order = true;
|
||||
// sconf.waste_increment = 0.005;
|
||||
// sconf.waste_increment = 0.01;
|
||||
|
||||
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;
|
||||
})/*.useMinimumBoundigBoxRotation()*/;
|
||||
});
|
||||
|
||||
// findMinimumBoundingBoxRotations(input.begin(), input.end());
|
||||
|
||||
Benchmark bench;
|
||||
|
||||
@ -693,7 +158,7 @@ void arrangeRectangles() {
|
||||
Packer::ResultType result;
|
||||
|
||||
try {
|
||||
result = arrange.arrange(input.begin(), input.end());
|
||||
result = arrange.execute(input.begin(), input.end());
|
||||
} catch(GeometryException& ge) {
|
||||
std::cerr << "Geometry error: " << ge.what() << std::endl;
|
||||
return ;
|
||||
@ -707,7 +172,7 @@ void arrangeRectangles() {
|
||||
std::vector<double> eff;
|
||||
eff.reserve(result.size());
|
||||
|
||||
auto bin_area = ShapeLike::area<PolygonImpl>(bin);
|
||||
auto bin_area = sl::area(bin);
|
||||
for(auto& r : result) {
|
||||
double a = 0;
|
||||
std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); });
|
||||
@ -728,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 = ShapeLike::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!"
|
||||
@ -744,13 +209,10 @@ void arrangeRectangles() {
|
||||
SVGWriter svgw(conf);
|
||||
svgw.setSize(Box(250*SCALE, 210*SCALE));
|
||||
svgw.writePackGroup(result);
|
||||
// std::for_each(input.begin(), input.end(), [&svgw](Item& item){ svgw.writeItem(item);});
|
||||
svgw.save("out");
|
||||
}
|
||||
|
||||
int main(void /*int argc, char **argv*/) {
|
||||
arrangeRectangles();
|
||||
// findDegenerateCase();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ using Point = PointImpl;
|
||||
using Coord = TCoord<PointImpl>;
|
||||
using Box = _Box<PointImpl>;
|
||||
using Segment = _Segment<PointImpl>;
|
||||
using Circle = _Circle<PointImpl>;
|
||||
|
||||
using Item = _Item<PolygonImpl>;
|
||||
using Rectangle = _Rectangle<PolygonImpl>;
|
||||
@ -29,15 +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>;
|
||||
|
||||
//template<NfpLevel lvl = NfpLevel::BOTH_CONCAVE_WITH_HOLES>
|
||||
//using NofitPolyPlacer = strategies::_NofitPolyPlacer<PolygonImpl, lvl>;
|
||||
using NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl>;
|
||||
using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
|
||||
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ using libnest2d::setX;
|
||||
using libnest2d::setY;
|
||||
using Box = libnest2d::_Box<PointImpl>;
|
||||
using Segment = libnest2d::_Segment<PointImpl>;
|
||||
using Shapes = libnest2d::Nfp::Shapes<PolygonImpl>;
|
||||
using Shapes = libnest2d::nfp::Shapes<PolygonImpl>;
|
||||
|
||||
}
|
||||
|
||||
@ -241,11 +241,11 @@ template<> struct tag<bp2d::PolygonImpl> {
|
||||
|
||||
template<> struct exterior_ring<bp2d::PolygonImpl> {
|
||||
static inline bp2d::PathImpl& get(bp2d::PolygonImpl& p) {
|
||||
return libnest2d::ShapeLike::getContour(p);
|
||||
return libnest2d::shapelike::getContour(p);
|
||||
}
|
||||
|
||||
static inline bp2d::PathImpl const& get(bp2d::PolygonImpl const& p) {
|
||||
return libnest2d::ShapeLike::getContour(p);
|
||||
return libnest2d::shapelike::getContour(p);
|
||||
}
|
||||
};
|
||||
|
||||
@ -271,13 +271,13 @@ struct interior_rings<bp2d::PolygonImpl> {
|
||||
static inline libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
|
||||
bp2d::PolygonImpl& p)
|
||||
{
|
||||
return libnest2d::ShapeLike::holes(p);
|
||||
return libnest2d::shapelike::holes(p);
|
||||
}
|
||||
|
||||
static inline const libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
|
||||
bp2d::PolygonImpl const& p)
|
||||
{
|
||||
return libnest2d::ShapeLike::holes(p);
|
||||
return libnest2d::shapelike::holes(p);
|
||||
}
|
||||
};
|
||||
|
||||
@ -311,83 +311,77 @@ struct range_value<bp2d::Shapes> {
|
||||
|
||||
namespace libnest2d { // Now the algorithms that boost can provide...
|
||||
|
||||
namespace pointlike {
|
||||
template<>
|
||||
inline double PointLike::distance(const PointImpl& p1,
|
||||
const PointImpl& p2 )
|
||||
inline double distance(const PointImpl& p1, const PointImpl& p2 )
|
||||
{
|
||||
return boost::geometry::distance(p1, p2);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double PointLike::distance(const PointImpl& p,
|
||||
const bp2d::Segment& seg )
|
||||
inline double distance(const PointImpl& p, const bp2d::Segment& seg )
|
||||
{
|
||||
return boost::geometry::distance(p, seg);
|
||||
}
|
||||
}
|
||||
|
||||
namespace shapelike {
|
||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||
template<>
|
||||
inline bool ShapeLike::intersects(const PathImpl& sh1,
|
||||
const PathImpl& sh2)
|
||||
inline bool intersects(const PathImpl& sh1, const PathImpl& sh2)
|
||||
{
|
||||
return boost::geometry::intersects(sh1, sh2);
|
||||
}
|
||||
|
||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||
template<>
|
||||
inline bool ShapeLike::intersects(const PolygonImpl& sh1,
|
||||
const PolygonImpl& sh2)
|
||||
inline bool intersects(const PolygonImpl& sh1, const PolygonImpl& sh2)
|
||||
{
|
||||
return boost::geometry::intersects(sh1, sh2);
|
||||
}
|
||||
|
||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||
template<>
|
||||
inline bool ShapeLike::intersects(const bp2d::Segment& s1,
|
||||
const bp2d::Segment& s2)
|
||||
inline bool intersects(const bp2d::Segment& s1, const bp2d::Segment& s2)
|
||||
{
|
||||
return boost::geometry::intersects(s1, s2);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_BOOST_AREA
|
||||
template<>
|
||||
inline double ShapeLike::area(const PolygonImpl& shape)
|
||||
inline double area(const PolygonImpl& shape, const PolygonTag&)
|
||||
{
|
||||
return boost::geometry::area(shape);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<>
|
||||
inline bool ShapeLike::isInside<PolygonImpl>(const PointImpl& point,
|
||||
const PolygonImpl& shape)
|
||||
inline bool isInside(const PointImpl& point, const PolygonImpl& shape)
|
||||
{
|
||||
return boost::geometry::within(point, shape);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ShapeLike::isInside(const PolygonImpl& sh1,
|
||||
const PolygonImpl& sh2)
|
||||
inline bool isInside(const PolygonImpl& sh1, const PolygonImpl& sh2)
|
||||
{
|
||||
return boost::geometry::within(sh1, sh2);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ShapeLike::touches( const PolygonImpl& sh1,
|
||||
const PolygonImpl& sh2)
|
||||
inline bool touches(const PolygonImpl& sh1, const PolygonImpl& sh2)
|
||||
{
|
||||
return boost::geometry::touches(sh1, sh2);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ShapeLike::touches( const PointImpl& point,
|
||||
const PolygonImpl& shape)
|
||||
inline bool touches( const PointImpl& point, const PolygonImpl& shape)
|
||||
{
|
||||
return boost::geometry::touches(point, shape);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_BOOST_BOUNDING_BOX
|
||||
template<>
|
||||
inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh)
|
||||
inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&)
|
||||
{
|
||||
bp2d::Box b;
|
||||
boost::geometry::envelope(sh, b);
|
||||
@ -395,7 +389,8 @@ inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh)
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bp2d::Box ShapeLike::boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
|
||||
inline bp2d::Box boundingBox<bp2d::Shapes>(const bp2d::Shapes& shapes,
|
||||
const MultiPolygonTag&)
|
||||
{
|
||||
bp2d::Box b;
|
||||
boost::geometry::envelope(shapes, b);
|
||||
@ -405,7 +400,7 @@ inline bp2d::Box ShapeLike::boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
|
||||
|
||||
#ifndef DISABLE_BOOST_CONVEX_HULL
|
||||
template<>
|
||||
inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh)
|
||||
inline PolygonImpl convexHull(const PolygonImpl& sh, const PolygonTag&)
|
||||
{
|
||||
PolygonImpl ret;
|
||||
boost::geometry::convex_hull(sh, ret);
|
||||
@ -413,7 +408,8 @@ inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh)
|
||||
}
|
||||
|
||||
template<>
|
||||
inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes)
|
||||
inline PolygonImpl convexHull(const TMultiShape<PolygonImpl>& shapes,
|
||||
const MultiPolygonTag&)
|
||||
{
|
||||
PolygonImpl ret;
|
||||
boost::geometry::convex_hull(shapes, ret);
|
||||
@ -421,56 +417,17 @@ inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_BOOST_ROTATE
|
||||
template<>
|
||||
inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
|
||||
{
|
||||
namespace trans = boost::geometry::strategy::transform;
|
||||
|
||||
PolygonImpl cpy = sh;
|
||||
trans::rotate_transformer<boost::geometry::radian, Radians, 2, 2>
|
||||
rotate(rads);
|
||||
|
||||
boost::geometry::transform(cpy, sh, rotate);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_BOOST_TRANSLATE
|
||||
template<>
|
||||
inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
|
||||
{
|
||||
namespace trans = boost::geometry::strategy::transform;
|
||||
|
||||
PolygonImpl cpy = sh;
|
||||
trans::translate_transformer<bp2d::Coord, 2, 2> translate(
|
||||
bp2d::getX(offs), bp2d::getY(offs));
|
||||
|
||||
boost::geometry::transform(cpy, sh, translate);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_BOOST_OFFSET
|
||||
template<>
|
||||
inline void ShapeLike::offset(PolygonImpl& sh, bp2d::Coord distance)
|
||||
inline void offset(PolygonImpl& sh, bp2d::Coord distance)
|
||||
{
|
||||
PolygonImpl cpy = sh;
|
||||
boost::geometry::buffer(cpy, sh, distance);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_BOOST_NFP_MERGE
|
||||
template<>
|
||||
inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes,
|
||||
const PolygonImpl& sh)
|
||||
{
|
||||
bp2d::Shapes retv;
|
||||
boost::geometry::union_(shapes, sh, retv);
|
||||
return retv;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_BOOST_SERIALIZE
|
||||
template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
|
||||
template<> inline std::string serialize<libnest2d::Formats::SVG>(
|
||||
const PolygonImpl& sh, double scale)
|
||||
{
|
||||
std::stringstream ss;
|
||||
@ -482,14 +439,14 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
|
||||
|
||||
Polygonf::ring_type ring;
|
||||
Polygonf::inner_container_type holes;
|
||||
ring.reserve(ShapeLike::contourVertexCount(sh));
|
||||
ring.reserve(shapelike::contourVertexCount(sh));
|
||||
|
||||
for(auto it = ShapeLike::cbegin(sh); it != ShapeLike::cend(sh); it++) {
|
||||
for(auto it = shapelike::cbegin(sh); it != shapelike::cend(sh); it++) {
|
||||
auto& v = *it;
|
||||
ring.emplace_back(getX(v)*scale, getY(v)*scale);
|
||||
};
|
||||
|
||||
auto H = ShapeLike::holes(sh);
|
||||
auto H = shapelike::holes(sh);
|
||||
for(PathImpl& h : H ) {
|
||||
Polygonf::ring_type hf;
|
||||
for(auto it = h.begin(); it != h.end(); it++) {
|
||||
@ -512,21 +469,47 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
|
||||
|
||||
#ifndef DISABLE_BOOST_UNSERIALIZE
|
||||
template<>
|
||||
inline void ShapeLike::unserialize<libnest2d::Formats::SVG>(
|
||||
inline void unserialize<libnest2d::Formats::SVG>(
|
||||
PolygonImpl& sh,
|
||||
const std::string& str)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
template<> inline std::pair<bool, std::string>
|
||||
ShapeLike::isValid(const PolygonImpl& sh)
|
||||
template<> inline std::pair<bool, std::string> isValid(const PolygonImpl& sh)
|
||||
{
|
||||
std::string message;
|
||||
bool ret = boost::geometry::is_valid(sh, message);
|
||||
|
||||
return {ret, message};
|
||||
}
|
||||
}
|
||||
|
||||
namespace nfp {
|
||||
|
||||
#ifndef DISABLE_BOOST_NFP_MERGE
|
||||
|
||||
// Warning: I could not get boost union_ to work. Geometries will overlap.
|
||||
template<>
|
||||
inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes,
|
||||
const PolygonImpl& sh)
|
||||
{
|
||||
bp2d::Shapes retv;
|
||||
boost::geometry::union_(shapes, sh, retv);
|
||||
return retv;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes)
|
||||
{
|
||||
bp2d::Shapes retv;
|
||||
boost::geometry::union_(shapes, shapes.back(), retv);
|
||||
return retv;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -99,49 +99,54 @@ template<> struct PointType<PolygonImpl> {
|
||||
using Type = PointImpl;
|
||||
};
|
||||
|
||||
// Type of vertex iterator used by Clipper
|
||||
template<> struct VertexIteratorType<PolygonImpl> {
|
||||
using Type = ClipperLib::Path::iterator;
|
||||
};
|
||||
|
||||
// Type of vertex iterator used by Clipper
|
||||
template<> struct VertexConstIteratorType<PolygonImpl> {
|
||||
using Type = ClipperLib::Path::const_iterator;
|
||||
template<> struct PointType<PointImpl> {
|
||||
using Type = PointImpl;
|
||||
};
|
||||
|
||||
template<> struct CountourType<PolygonImpl> {
|
||||
using Type = PathImpl;
|
||||
};
|
||||
|
||||
// Tell binpack2d how to extract the X coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl> PointLike::x(const PointImpl& p)
|
||||
template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; };
|
||||
|
||||
template<> struct ShapeTag<TMultiShape<PolygonImpl>> {
|
||||
using Type = MultiPolygonTag;
|
||||
};
|
||||
|
||||
template<> struct PointType<TMultiShape<PolygonImpl>> {
|
||||
using Type = PointImpl;
|
||||
};
|
||||
|
||||
template<> struct HolesContainer<PolygonImpl> {
|
||||
using Type = ClipperLib::Paths;
|
||||
};
|
||||
|
||||
namespace pointlike {
|
||||
|
||||
// Tell libnest2d how to extract the X coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl> x(const PointImpl& p)
|
||||
{
|
||||
return p.X;
|
||||
}
|
||||
|
||||
// Tell binpack2d how to extract the Y coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl> PointLike::y(const PointImpl& p)
|
||||
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl> y(const PointImpl& p)
|
||||
{
|
||||
return p.Y;
|
||||
}
|
||||
|
||||
// Tell binpack2d how to extract the X coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl>& PointLike::x(PointImpl& p)
|
||||
// Tell libnest2d how to extract the X coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl>& x(PointImpl& p)
|
||||
{
|
||||
return p.X;
|
||||
}
|
||||
|
||||
// Tell binpack2d how to extract the Y coord from a ClipperPoint object
|
||||
template<>
|
||||
inline TCoord<PointImpl>& PointLike::y(PointImpl& p)
|
||||
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl>& y(PointImpl& p)
|
||||
{
|
||||
return p.Y;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void ShapeLike::reserve(PolygonImpl& sh, size_t vertex_capacity)
|
||||
{
|
||||
return sh.Contour.reserve(vertex_capacity);
|
||||
}
|
||||
|
||||
#define DISABLE_BOOST_AREA
|
||||
@ -175,16 +180,24 @@ inline double area<Orientation::COUNTER_CLOCKWISE>(const PolygonImpl& sh) {
|
||||
|
||||
return ClipperLib::Area(sh.Contour) + a;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Tell binpack2d how to make string out of a ClipperPolygon object
|
||||
template<>
|
||||
inline double ShapeLike::area(const PolygonImpl& sh) {
|
||||
namespace shapelike {
|
||||
|
||||
template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity)
|
||||
{
|
||||
return sh.Contour.reserve(vertex_capacity);
|
||||
}
|
||||
|
||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||
template<> inline double area(const PolygonImpl& sh, const PolygonTag&)
|
||||
{
|
||||
return _smartarea::area<OrientationType<PolygonImpl>::Value>(sh);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
|
||||
template<> inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance)
|
||||
{
|
||||
#define DISABLE_BOOST_OFFSET
|
||||
|
||||
using ClipperLib::ClipperOffset;
|
||||
@ -234,7 +247,8 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
|
||||
}
|
||||
|
||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||
template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
|
||||
template<> inline std::string toString(const PolygonImpl& sh)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "Contour {\n";
|
||||
@ -257,37 +271,8 @@ template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
|
||||
}
|
||||
|
||||
template<>
|
||||
inline TVertexIterator<PolygonImpl> ShapeLike::begin(PolygonImpl& sh)
|
||||
inline PolygonImpl create(const PathImpl& path, const HoleStore& holes)
|
||||
{
|
||||
return sh.Contour.begin();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline TVertexIterator<PolygonImpl> ShapeLike::end(PolygonImpl& sh)
|
||||
{
|
||||
return sh.Contour.end();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline TVertexConstIterator<PolygonImpl> ShapeLike::cbegin(
|
||||
const PolygonImpl& sh)
|
||||
{
|
||||
return sh.Contour.cbegin();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline TVertexConstIterator<PolygonImpl> ShapeLike::cend(
|
||||
const PolygonImpl& sh)
|
||||
{
|
||||
return sh.Contour.cend();
|
||||
}
|
||||
|
||||
template<> struct HolesContainer<PolygonImpl> {
|
||||
using Type = ClipperLib::Paths;
|
||||
};
|
||||
|
||||
template<> inline PolygonImpl ShapeLike::create(const PathImpl& path,
|
||||
const HoleStore& holes) {
|
||||
PolygonImpl p;
|
||||
p.Contour = path;
|
||||
|
||||
@ -308,8 +293,7 @@ template<> inline PolygonImpl ShapeLike::create(const PathImpl& path,
|
||||
return p;
|
||||
}
|
||||
|
||||
template<> inline PolygonImpl ShapeLike::create( PathImpl&& path,
|
||||
HoleStore&& holes) {
|
||||
template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) {
|
||||
PolygonImpl p;
|
||||
p.Contour.swap(path);
|
||||
|
||||
@ -331,49 +315,49 @@ template<> inline PolygonImpl ShapeLike::create( PathImpl&& path,
|
||||
return p;
|
||||
}
|
||||
|
||||
template<> inline const THolesContainer<PolygonImpl>&
|
||||
ShapeLike::holes(const PolygonImpl& sh)
|
||||
template<>
|
||||
inline const THolesContainer<PolygonImpl>& holes(const PolygonImpl& sh)
|
||||
{
|
||||
return sh.Holes;
|
||||
}
|
||||
|
||||
template<> inline THolesContainer<PolygonImpl>&
|
||||
ShapeLike::holes(PolygonImpl& sh)
|
||||
template<> inline THolesContainer<PolygonImpl>& holes(PolygonImpl& sh)
|
||||
{
|
||||
return sh.Holes;
|
||||
}
|
||||
|
||||
template<> inline TContour<PolygonImpl>&
|
||||
ShapeLike::getHole(PolygonImpl& sh, unsigned long idx)
|
||||
template<>
|
||||
inline TContour<PolygonImpl>& getHole(PolygonImpl& sh, unsigned long idx)
|
||||
{
|
||||
return sh.Holes[idx];
|
||||
}
|
||||
|
||||
template<> inline const TContour<PolygonImpl>&
|
||||
ShapeLike::getHole(const PolygonImpl& sh, unsigned long idx)
|
||||
template<>
|
||||
inline const TContour<PolygonImpl>& getHole(const PolygonImpl& sh,
|
||||
unsigned long idx)
|
||||
{
|
||||
return sh.Holes[idx];
|
||||
}
|
||||
|
||||
template<> inline size_t ShapeLike::holeCount(const PolygonImpl& sh)
|
||||
template<> inline size_t holeCount(const PolygonImpl& sh)
|
||||
{
|
||||
return sh.Holes.size();
|
||||
}
|
||||
|
||||
template<> inline PathImpl& ShapeLike::getContour(PolygonImpl& sh)
|
||||
template<> inline PathImpl& getContour(PolygonImpl& sh)
|
||||
{
|
||||
return sh.Contour;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline const PathImpl& ShapeLike::getContour(const PolygonImpl& sh)
|
||||
inline const PathImpl& getContour(const PolygonImpl& sh)
|
||||
{
|
||||
return sh.Contour;
|
||||
}
|
||||
|
||||
#define DISABLE_BOOST_TRANSLATE
|
||||
template<>
|
||||
inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
|
||||
inline void translate(PolygonImpl& sh, const PointImpl& offs)
|
||||
{
|
||||
for(auto& p : sh.Contour) { p += offs; }
|
||||
for(auto& hole : sh.Holes) for(auto& p : hole) { p += offs; }
|
||||
@ -381,7 +365,7 @@ inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
|
||||
|
||||
#define DISABLE_BOOST_ROTATE
|
||||
template<>
|
||||
inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
|
||||
inline void rotate(PolygonImpl& sh, const Radians& rads)
|
||||
{
|
||||
using Coord = TCoord<PointImpl>;
|
||||
|
||||
@ -402,9 +386,11 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace shapelike
|
||||
|
||||
#define DISABLE_BOOST_NFP_MERGE
|
||||
inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||
Nfp::Shapes<PolygonImpl> retv;
|
||||
inline std::vector<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||
shapelike::Shapes<PolygonImpl> retv;
|
||||
|
||||
ClipperLib::PolyTree result;
|
||||
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
|
||||
@ -438,8 +424,10 @@ inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||
return retv;
|
||||
}
|
||||
|
||||
template<> inline Nfp::Shapes<PolygonImpl>
|
||||
Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
|
||||
namespace nfp {
|
||||
|
||||
template<> inline std::vector<PolygonImpl>
|
||||
merge(const std::vector<PolygonImpl>& shapes)
|
||||
{
|
||||
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
|
||||
|
||||
@ -461,6 +449,8 @@ Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//#define DISABLE_BOOST_SERIALIZE
|
||||
//#define DISABLE_BOOST_UNSERIALIZE
|
||||
|
||||
|
@ -22,34 +22,12 @@ template<class GeomType>
|
||||
using TCoord = typename CoordType<remove_cvref_t<GeomType>>::Type;
|
||||
|
||||
/// Getting the type of point structure used by a shape.
|
||||
template<class Shape> struct PointType { /*using Type = void;*/ };
|
||||
template<class Sh> struct PointType { using Type = typename Sh::PointType; };
|
||||
|
||||
/// TPoint<ShapeClass> as shorthand for `typename PointType<ShapeClass>::Type`.
|
||||
template<class Shape>
|
||||
using TPoint = typename PointType<remove_cvref_t<Shape>>::Type;
|
||||
|
||||
/// Getting the VertexIterator type of a shape class.
|
||||
template<class Shape> struct VertexIteratorType { /*using Type = void;*/ };
|
||||
|
||||
/// Getting the const vertex iterator for a shape class.
|
||||
template<class Shape> struct VertexConstIteratorType {/* using Type = void;*/ };
|
||||
|
||||
/**
|
||||
* TVertexIterator<Shape> as shorthand for
|
||||
* `typename VertexIteratorType<Shape>::Type`
|
||||
*/
|
||||
template<class Shape>
|
||||
using TVertexIterator =
|
||||
typename VertexIteratorType<remove_cvref_t<Shape>>::Type;
|
||||
|
||||
/**
|
||||
* \brief TVertexConstIterator<Shape> as shorthand for
|
||||
* `typename VertexConstIteratorType<Shape>::Type`
|
||||
*/
|
||||
template<class ShapeClass>
|
||||
using TVertexConstIterator =
|
||||
typename VertexConstIteratorType<remove_cvref_t<ShapeClass>>::Type;
|
||||
|
||||
/**
|
||||
* \brief A point pair base class for other point pairs (segment, box, ...).
|
||||
* \tparam RawPoint The actual point type to use.
|
||||
@ -60,6 +38,17 @@ struct PointPair {
|
||||
RawPoint p2;
|
||||
};
|
||||
|
||||
struct PolygonTag {};
|
||||
struct MultiPolygonTag {};
|
||||
struct BoxTag {};
|
||||
struct CircleTag {};
|
||||
|
||||
template<class Shape> struct ShapeTag { using Type = typename Shape::Tag; };
|
||||
template<class S> using Tag = typename ShapeTag<S>::Type;
|
||||
|
||||
template<class S> struct MultiShape { using Type = std::vector<S>; };
|
||||
template<class S> using TMultiShape = typename MultiShape<S>::Type;
|
||||
|
||||
/**
|
||||
* \brief An abstraction of a box;
|
||||
*/
|
||||
@ -69,6 +58,9 @@ class _Box: PointPair<RawPoint> {
|
||||
using PointPair<RawPoint>::p2;
|
||||
public:
|
||||
|
||||
using Tag = BoxTag;
|
||||
using PointType = RawPoint;
|
||||
|
||||
inline _Box() = default;
|
||||
inline _Box(const RawPoint& p, const RawPoint& pp):
|
||||
PointPair<RawPoint>({p, pp}) {}
|
||||
@ -98,6 +90,9 @@ class _Circle {
|
||||
double radius_ = 0;
|
||||
public:
|
||||
|
||||
using Tag = CircleTag;
|
||||
using PointType = RawPoint;
|
||||
|
||||
_Circle() = default;
|
||||
|
||||
_Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
|
||||
@ -109,7 +104,7 @@ public:
|
||||
inline void radius(double r) { radius_ = r; }
|
||||
|
||||
inline double area() const BP2D_NOEXCEPT {
|
||||
return 2.0*Pi*radius_;
|
||||
return 2.0*Pi*radius_*radius_;
|
||||
}
|
||||
};
|
||||
|
||||
@ -123,6 +118,8 @@ class _Segment: PointPair<RawPoint> {
|
||||
mutable Radians angletox_ = std::nan("");
|
||||
public:
|
||||
|
||||
using PointType = RawPoint;
|
||||
|
||||
inline _Segment() = default;
|
||||
|
||||
inline _Segment(const RawPoint& p, const RawPoint& pp):
|
||||
@ -156,36 +153,36 @@ public:
|
||||
inline double length();
|
||||
};
|
||||
|
||||
// This struct serves as a namespace. The only difference is that is can be
|
||||
// This struct serves almost as a namespace. The only difference is that is can
|
||||
// used in friend declarations.
|
||||
struct PointLike {
|
||||
namespace pointlike {
|
||||
|
||||
template<class RawPoint>
|
||||
static TCoord<RawPoint> x(const RawPoint& p)
|
||||
inline TCoord<RawPoint> x(const RawPoint& p)
|
||||
{
|
||||
return p(0);
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
static TCoord<RawPoint> y(const RawPoint& p)
|
||||
inline TCoord<RawPoint> y(const RawPoint& p)
|
||||
{
|
||||
return p(1);
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
static TCoord<RawPoint>& x(RawPoint& p)
|
||||
inline TCoord<RawPoint>& x(RawPoint& p)
|
||||
{
|
||||
return p(0);
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
static TCoord<RawPoint>& y(RawPoint& p)
|
||||
inline TCoord<RawPoint>& y(RawPoint& p)
|
||||
{
|
||||
return p(1);
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
static double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/)
|
||||
inline double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/)
|
||||
{
|
||||
static_assert(always_false<RawPoint>::value,
|
||||
"PointLike::distance(point, point) unimplemented!");
|
||||
@ -193,7 +190,7 @@ struct PointLike {
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
static double distance(const RawPoint& /*p1*/,
|
||||
inline double distance(const RawPoint& /*p1*/,
|
||||
const _Segment<RawPoint>& /*s*/)
|
||||
{
|
||||
static_assert(always_false<RawPoint>::value,
|
||||
@ -202,13 +199,13 @@ struct PointLike {
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
static std::pair<TCoord<RawPoint>, bool> horizontalDistance(
|
||||
inline std::pair<TCoord<RawPoint>, bool> horizontalDistance(
|
||||
const RawPoint& p, const _Segment<RawPoint>& s)
|
||||
{
|
||||
using Unit = TCoord<RawPoint>;
|
||||
auto x = PointLike::x(p), y = PointLike::y(p);
|
||||
auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first());
|
||||
auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second());
|
||||
auto x = pointlike::x(p), y = pointlike::y(p);
|
||||
auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first());
|
||||
auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second());
|
||||
|
||||
TCoord<RawPoint> ret;
|
||||
|
||||
@ -228,13 +225,13 @@ struct PointLike {
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
static std::pair<TCoord<RawPoint>, bool> verticalDistance(
|
||||
inline std::pair<TCoord<RawPoint>, bool> verticalDistance(
|
||||
const RawPoint& p, const _Segment<RawPoint>& s)
|
||||
{
|
||||
using Unit = TCoord<RawPoint>;
|
||||
auto x = PointLike::x(p), y = PointLike::y(p);
|
||||
auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first());
|
||||
auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second());
|
||||
auto x = pointlike::x(p), y = pointlike::y(p);
|
||||
auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first());
|
||||
auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second());
|
||||
|
||||
TCoord<RawPoint> ret;
|
||||
|
||||
@ -252,36 +249,36 @@ struct PointLike {
|
||||
|
||||
return {ret, true};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
TCoord<RawPoint> _Box<RawPoint>::width() const BP2D_NOEXCEPT
|
||||
{
|
||||
return PointLike::x(maxCorner()) - PointLike::x(minCorner());
|
||||
return pointlike::x(maxCorner()) - pointlike::x(minCorner());
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
TCoord<RawPoint> _Box<RawPoint>::height() const BP2D_NOEXCEPT
|
||||
{
|
||||
return PointLike::y(maxCorner()) - PointLike::y(minCorner());
|
||||
return pointlike::y(maxCorner()) - pointlike::y(minCorner());
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
TCoord<RawPoint> getX(const RawPoint& p) { return PointLike::x<RawPoint>(p); }
|
||||
TCoord<RawPoint> getX(const RawPoint& p) { return pointlike::x<RawPoint>(p); }
|
||||
|
||||
template<class RawPoint>
|
||||
TCoord<RawPoint> getY(const RawPoint& p) { return PointLike::y<RawPoint>(p); }
|
||||
TCoord<RawPoint> getY(const RawPoint& p) { return pointlike::y<RawPoint>(p); }
|
||||
|
||||
template<class RawPoint>
|
||||
void setX(RawPoint& p, const TCoord<RawPoint>& val)
|
||||
{
|
||||
PointLike::x<RawPoint>(p) = val;
|
||||
pointlike::x<RawPoint>(p) = val;
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
void setY(RawPoint& p, const TCoord<RawPoint>& val)
|
||||
{
|
||||
PointLike::y<RawPoint>(p) = val;
|
||||
pointlike::y<RawPoint>(p) = val;
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
@ -303,7 +300,7 @@ inline Radians _Segment<RawPoint>::angleToXaxis() const
|
||||
template<class RawPoint>
|
||||
inline double _Segment<RawPoint>::length()
|
||||
{
|
||||
return PointLike::distance(first(), second());
|
||||
return pointlike::distance(first(), second());
|
||||
}
|
||||
|
||||
template<class RawPoint>
|
||||
@ -313,9 +310,9 @@ inline RawPoint _Box<RawPoint>::center() const BP2D_NOEXCEPT {
|
||||
|
||||
using Coord = TCoord<RawPoint>;
|
||||
|
||||
RawPoint ret = {
|
||||
static_cast<Coord>( std::round((getX(minc) + getX(maxc))/2.0) ),
|
||||
static_cast<Coord>( std::round((getY(minc) + getY(maxc))/2.0) )
|
||||
RawPoint ret = { // No rounding here, we dont know if these are int coords
|
||||
static_cast<Coord>( (getX(minc) + getX(maxc))/2.0 ),
|
||||
static_cast<Coord>( (getY(minc) + getY(maxc))/2.0 )
|
||||
};
|
||||
|
||||
return ret;
|
||||
@ -356,124 +353,125 @@ enum class Formats {
|
||||
|
||||
// This struct serves as a namespace. The only difference is that it can be
|
||||
// used in friend declarations and can be aliased at class scope.
|
||||
struct ShapeLike {
|
||||
namespace shapelike {
|
||||
|
||||
template<class RawShape>
|
||||
using Shapes = std::vector<RawShape>;
|
||||
using Shapes = TMultiShape<RawShape>;
|
||||
|
||||
template<class RawShape>
|
||||
static RawShape create(const TContour<RawShape>& contour,
|
||||
inline RawShape create(const TContour<RawShape>& contour,
|
||||
const THolesContainer<RawShape>& holes)
|
||||
{
|
||||
return RawShape(contour, holes);
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static RawShape create(TContour<RawShape>&& contour,
|
||||
inline RawShape create(TContour<RawShape>&& contour,
|
||||
THolesContainer<RawShape>&& holes)
|
||||
{
|
||||
return RawShape(contour, holes);
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static RawShape create(const TContour<RawShape>& contour)
|
||||
inline RawShape create(const TContour<RawShape>& contour)
|
||||
{
|
||||
return create<RawShape>(contour, {});
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static RawShape create(TContour<RawShape>&& contour)
|
||||
inline RawShape create(TContour<RawShape>&& contour)
|
||||
{
|
||||
return create<RawShape>(contour, {});
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static THolesContainer<RawShape>& holes(RawShape& /*sh*/)
|
||||
inline THolesContainer<RawShape>& holes(RawShape& /*sh*/)
|
||||
{
|
||||
static THolesContainer<RawShape> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
|
||||
inline const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
|
||||
{
|
||||
static THolesContainer<RawShape> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
|
||||
inline TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
|
||||
{
|
||||
return holes(sh)[idx];
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const TContour<RawShape>& getHole(const RawShape& sh,
|
||||
inline const TContour<RawShape>& getHole(const RawShape& sh,
|
||||
unsigned long idx)
|
||||
{
|
||||
return holes(sh)[idx];
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static size_t holeCount(const RawShape& sh)
|
||||
inline size_t holeCount(const RawShape& sh)
|
||||
{
|
||||
return holes(sh).size();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TContour<RawShape>& getContour(RawShape& sh)
|
||||
inline TContour<RawShape>& getContour(RawShape& sh)
|
||||
{
|
||||
return sh;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const TContour<RawShape>& getContour(const RawShape& sh)
|
||||
inline const TContour<RawShape>& getContour(const RawShape& sh)
|
||||
{
|
||||
return sh;
|
||||
}
|
||||
|
||||
// Optional, does nothing by default
|
||||
template<class RawShape>
|
||||
static void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {}
|
||||
inline void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {}
|
||||
|
||||
template<class RawShape, class...Args>
|
||||
static void addVertex(RawShape& sh, Args...args)
|
||||
inline void addVertex(RawShape& sh, Args...args)
|
||||
{
|
||||
return getContour(sh).emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TVertexIterator<RawShape> begin(RawShape& sh)
|
||||
inline typename TContour<RawShape>::iterator begin(RawShape& sh)
|
||||
{
|
||||
return sh.begin();
|
||||
return getContour(sh).begin();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TVertexIterator<RawShape> end(RawShape& sh)
|
||||
inline typename TContour<RawShape>::iterator end(RawShape& sh)
|
||||
{
|
||||
return sh.end();
|
||||
return getContour(sh).end();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TVertexConstIterator<RawShape> cbegin(const RawShape& sh)
|
||||
inline typename TContour<RawShape>::const_iterator
|
||||
cbegin(const RawShape& sh)
|
||||
{
|
||||
return sh.cbegin();
|
||||
return getContour(sh).cbegin();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TVertexConstIterator<RawShape> cend(const RawShape& sh)
|
||||
inline typename TContour<RawShape>::const_iterator cend(const RawShape& sh)
|
||||
{
|
||||
return sh.cend();
|
||||
return getContour(sh).cend();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static std::string toString(const RawShape& /*sh*/)
|
||||
inline std::string toString(const RawShape& /*sh*/)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
template<Formats, class RawShape>
|
||||
static std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
|
||||
inline std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::serialize() unimplemented!");
|
||||
@ -481,14 +479,14 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<Formats, class RawShape>
|
||||
static void unserialize(RawShape& /*sh*/, const std::string& /*str*/)
|
||||
inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::unserialize() unimplemented!");
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static double area(const RawShape& /*sh*/)
|
||||
inline double area(const RawShape& /*sh*/, const PolygonTag&)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::area() unimplemented!");
|
||||
@ -496,7 +494,7 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
|
||||
inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::intersects() unimplemented!");
|
||||
@ -504,7 +502,7 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const TPoint<RawShape>& /*point*/,
|
||||
inline bool isInside(const TPoint<RawShape>& /*point*/,
|
||||
const RawShape& /*shape*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
@ -513,7 +511,7 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const RawShape& /*shape*/,
|
||||
inline bool isInside(const RawShape& /*shape*/,
|
||||
const RawShape& /*shape*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
@ -522,7 +520,7 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool touches( const RawShape& /*shape*/,
|
||||
inline bool touches( const RawShape& /*shape*/,
|
||||
const RawShape& /*shape*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
@ -531,7 +529,7 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool touches( const TPoint<RawShape>& /*point*/,
|
||||
inline bool touches( const TPoint<RawShape>& /*point*/,
|
||||
const RawShape& /*shape*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
@ -540,64 +538,66 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/)
|
||||
inline _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/,
|
||||
const PolygonTag&)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::boundingBox(shape) unimplemented!");
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static _Box<TPoint<RawShape>> boundingBox(const Shapes<RawShape>& /*sh*/)
|
||||
template<class RawShapes>
|
||||
inline _Box<TPoint<typename RawShapes::value_type>>
|
||||
boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
static_assert(always_false<RawShapes>::value,
|
||||
"ShapeLike::boundingBox(shapes) unimplemented!");
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static RawShape convexHull(const RawShape& /*sh*/)
|
||||
inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::convexHull(shape) unimplemented!");
|
||||
return RawShape();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static RawShape convexHull(const Shapes<RawShape>& /*sh*/)
|
||||
template<class RawShapes>
|
||||
inline typename RawShapes::value_type
|
||||
convexHull(const RawShapes& /*sh*/, const MultiPolygonTag&)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
static_assert(always_false<RawShapes>::value,
|
||||
"ShapeLike::convexHull(shapes) unimplemented!");
|
||||
return RawShape();
|
||||
return typename RawShapes::value_type();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
|
||||
inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::rotate() unimplemented!");
|
||||
}
|
||||
|
||||
template<class RawShape, class RawPoint>
|
||||
static void translate(RawShape& /*sh*/, const RawPoint& /*offs*/)
|
||||
inline void translate(RawShape& /*sh*/, const RawPoint& /*offs*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::translate() unimplemented!");
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
|
||||
inline void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::offset() unimplemented!");
|
||||
dout() << "The current geometry backend does not support offsetting!\n";
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static std::pair<bool, std::string> isValid(const RawShape& /*sh*/)
|
||||
inline std::pair<bool, std::string> isValid(const RawShape& /*sh*/)
|
||||
{
|
||||
return {false, "ShapeLike::isValid() unimplemented!"};
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline bool isConvex(const TContour<RawShape>& sh)
|
||||
inline bool isConvex(const TContour<RawShape>& sh)
|
||||
{
|
||||
using Vertex = TPoint<RawShape>;
|
||||
auto first = sh.begin();
|
||||
@ -633,43 +633,55 @@ struct ShapeLike {
|
||||
// No need to implement these
|
||||
// *************************************************************************
|
||||
|
||||
template<class RawShape>
|
||||
static inline _Box<TPoint<RawShape>> boundingBox(
|
||||
const _Box<TPoint<RawShape>>& box)
|
||||
template<class Box>
|
||||
inline Box boundingBox(const Box& box, const BoxTag& )
|
||||
{
|
||||
return box;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline _Box<TPoint<RawShape>> boundingBox(
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
template<class Circle>
|
||||
inline _Box<typename Circle::PointType> boundingBox(
|
||||
const Circle& circ, const CircleTag&)
|
||||
{
|
||||
using Coord = TCoord<TPoint<RawShape>>;
|
||||
TPoint<RawShape> pmin = {
|
||||
using Point = typename Circle::PointType;
|
||||
using Coord = TCoord<Point>;
|
||||
Point pmin = {
|
||||
static_cast<Coord>(getX(circ.center()) - circ.radius()),
|
||||
static_cast<Coord>(getY(circ.center()) - circ.radius()) };
|
||||
|
||||
TPoint<RawShape> pmax = {
|
||||
Point pmax = {
|
||||
static_cast<Coord>(getX(circ.center()) + circ.radius()),
|
||||
static_cast<Coord>(getY(circ.center()) + circ.radius()) };
|
||||
|
||||
return {pmin, pmax};
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline double area(const _Box<TPoint<RawShape>>& box)
|
||||
template<class S> // Dispatch function
|
||||
inline _Box<TPoint<S>> boundingBox(const S& sh)
|
||||
{
|
||||
return static_cast<double>(box.width() * box.height());
|
||||
return boundingBox(sh, Tag<S>() );
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline double area(const _Circle<TPoint<RawShape>>& circ)
|
||||
template<class Box>
|
||||
inline double area(const Box& box, const BoxTag& )
|
||||
{
|
||||
return box.area();
|
||||
}
|
||||
|
||||
template<class Circle>
|
||||
inline double area(const Circle& circ, const CircleTag& )
|
||||
{
|
||||
return circ.area();
|
||||
}
|
||||
|
||||
template<class RawShape> // Dispatching function
|
||||
inline double area(const RawShape& sh)
|
||||
{
|
||||
return area(sh, Tag<RawShape>());
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline double area(const Shapes<RawShape>& shapes)
|
||||
inline double area(const Shapes<RawShape>& shapes)
|
||||
{
|
||||
return std::accumulate(shapes.begin(), shapes.end(), 0.0,
|
||||
[](double a, const RawShape& b) {
|
||||
@ -678,14 +690,21 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const TPoint<RawShape>& point,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
inline auto convexHull(const RawShape& sh)
|
||||
-> decltype(convexHull(sh, Tag<RawShape>())) // TODO: C++14 could deduce
|
||||
{
|
||||
return PointLike::distance(point, circ.center()) < circ.radius();
|
||||
return convexHull(sh, Tag<RawShape>());
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const TPoint<RawShape>& point,
|
||||
inline bool isInside(const TPoint<RawShape>& point,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
return pointlike::distance(point, circ.center()) < circ.radius();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline bool isInside(const TPoint<RawShape>& point,
|
||||
const _Box<TPoint<RawShape>>& box)
|
||||
{
|
||||
auto px = getX(point);
|
||||
@ -699,7 +718,7 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const RawShape& sh,
|
||||
inline bool isInside(const RawShape& sh,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
return std::all_of(cbegin(sh), cend(sh),
|
||||
@ -709,7 +728,7 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const _Box<TPoint<RawShape>>& box,
|
||||
inline bool isInside(const _Box<TPoint<RawShape>>& box,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
return isInside<RawShape>(box.minCorner(), circ) &&
|
||||
@ -717,7 +736,7 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const _Box<TPoint<RawShape>>& ibb,
|
||||
inline bool isInside(const _Box<TPoint<RawShape>>& ibb,
|
||||
const _Box<TPoint<RawShape>>& box)
|
||||
{
|
||||
auto iminX = getX(ibb.minCorner());
|
||||
@ -734,31 +753,31 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape> // Potential O(1) implementation may exist
|
||||
static inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx)
|
||||
inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx)
|
||||
{
|
||||
return *(begin(sh) + idx);
|
||||
}
|
||||
|
||||
template<class RawShape> // Potential O(1) implementation may exist
|
||||
static inline const TPoint<RawShape>& vertex(const RawShape& sh,
|
||||
inline const TPoint<RawShape>& vertex(const RawShape& sh,
|
||||
unsigned long idx)
|
||||
{
|
||||
return *(cbegin(sh) + idx);
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline size_t contourVertexCount(const RawShape& sh)
|
||||
inline size_t contourVertexCount(const RawShape& sh)
|
||||
{
|
||||
return cend(sh) - cbegin(sh);
|
||||
}
|
||||
|
||||
template<class RawShape, class Fn>
|
||||
static inline void foreachContourVertex(RawShape& sh, Fn fn) {
|
||||
inline void foreachContourVertex(RawShape& sh, Fn fn) {
|
||||
for(auto it = begin(sh); it != end(sh); ++it) fn(*it);
|
||||
}
|
||||
|
||||
template<class RawShape, class Fn>
|
||||
static inline void foreachHoleVertex(RawShape& sh, Fn fn) {
|
||||
inline void foreachHoleVertex(RawShape& sh, Fn fn) {
|
||||
for(int i = 0; i < holeCount(sh); ++i) {
|
||||
auto& h = getHole(sh, i);
|
||||
for(auto it = begin(h); it != end(h); ++it) fn(*it);
|
||||
@ -766,12 +785,12 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape, class Fn>
|
||||
static inline void foreachContourVertex(const RawShape& sh, Fn fn) {
|
||||
inline void foreachContourVertex(const RawShape& sh, Fn fn) {
|
||||
for(auto it = cbegin(sh); it != cend(sh); ++it) fn(*it);
|
||||
}
|
||||
|
||||
template<class RawShape, class Fn>
|
||||
static inline void foreachHoleVertex(const RawShape& sh, Fn fn) {
|
||||
inline void foreachHoleVertex(const RawShape& sh, Fn fn) {
|
||||
for(int i = 0; i < holeCount(sh); ++i) {
|
||||
auto& h = getHole(sh, i);
|
||||
for(auto it = cbegin(h); it != cend(h); ++it) fn(*it);
|
||||
@ -779,18 +798,27 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape, class Fn>
|
||||
static inline void foreachVertex(RawShape& sh, Fn fn) {
|
||||
inline void foreachVertex(RawShape& sh, Fn fn) {
|
||||
foreachContourVertex(sh, fn);
|
||||
foreachHoleVertex(sh, fn);
|
||||
}
|
||||
|
||||
template<class RawShape, class Fn>
|
||||
static inline void foreachVertex(const RawShape& sh, Fn fn) {
|
||||
inline void foreachVertex(const RawShape& sh, Fn fn) {
|
||||
foreachContourVertex(sh, fn);
|
||||
foreachHoleVertex(sh, fn);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
#define DECLARE_MAIN_TYPES(T) \
|
||||
using Polygon = T; \
|
||||
using Point = TPoint<T>; \
|
||||
using Coord = TCoord<Point>; \
|
||||
using Contour = TContour<T>; \
|
||||
using Box = _Box<Point>; \
|
||||
using Circle = _Circle<Point>; \
|
||||
using Segment = _Segment<Point>; \
|
||||
using Polygons = TMultiShape<T>
|
||||
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,27 @@
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
namespace __nfp {
|
||||
// Do not specialize this...
|
||||
template<class RawShape>
|
||||
inline bool _vsort(const TPoint<RawShape>& v1, const TPoint<RawShape>& v2)
|
||||
{
|
||||
using Coord = TCoord<TPoint<RawShape>>;
|
||||
Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2);
|
||||
auto diff = y1 - y2;
|
||||
if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon())
|
||||
return x1 < x2;
|
||||
|
||||
return diff < 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of static methods for handling the no fit polygon creation.
|
||||
namespace nfp {
|
||||
|
||||
//namespace sl = shapelike;
|
||||
//namespace pl = pointlike;
|
||||
|
||||
/// The complexity level of a polygon that an NFP implementation can handle.
|
||||
enum class NfpLevel: unsigned {
|
||||
CONVEX_ONLY,
|
||||
@ -18,12 +39,17 @@ enum class NfpLevel: unsigned {
|
||||
BOTH_CONCAVE_WITH_HOLES
|
||||
};
|
||||
|
||||
/// A collection of static methods for handling the no fit polygon creation.
|
||||
struct Nfp {
|
||||
template<class RawShape>
|
||||
using NfpResult = std::pair<RawShape, TPoint<RawShape>>;
|
||||
|
||||
template<class RawShape> struct MaxNfpLevel {
|
||||
static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
|
||||
};
|
||||
|
||||
|
||||
// Shorthand for a pile of polygons
|
||||
template<class RawShape>
|
||||
using Shapes = typename ShapeLike::Shapes<RawShape>;
|
||||
using Shapes = TMultiShape<RawShape>;
|
||||
|
||||
/**
|
||||
* Merge a bunch of polygons with the specified additional polygon.
|
||||
@ -36,10 +62,10 @@ using Shapes = typename ShapeLike::Shapes<RawShape>;
|
||||
* mostly it will be a set containing only one big polygon but if the input
|
||||
* polygons are disjuct than the resulting set will contain more polygons.
|
||||
*/
|
||||
template<class RawShape>
|
||||
static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
|
||||
template<class RawShapes>
|
||||
inline RawShapes merge(const RawShapes& /*shc*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
static_assert(always_false<RawShapes>::value,
|
||||
"Nfp::merge(shapes, shape) unimplemented!");
|
||||
}
|
||||
|
||||
@ -55,12 +81,44 @@ static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
|
||||
* polygons are disjuct than the resulting set will contain more polygons.
|
||||
*/
|
||||
template<class RawShape>
|
||||
static Shapes<RawShape> merge(const Shapes<RawShape>& shc,
|
||||
const RawShape& sh)
|
||||
inline TMultiShape<RawShape> merge(const TMultiShape<RawShape>& shc,
|
||||
const RawShape& sh)
|
||||
{
|
||||
auto m = merge(shc);
|
||||
auto m = nfp::merge(shc);
|
||||
m.push_back(sh);
|
||||
return merge(m);
|
||||
return nfp::merge(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vertex of the polygon that is at the lowest values (bottom) in the Y
|
||||
* axis and if there are more than one vertices on the same Y coordinate than
|
||||
* the result will be the leftmost (with the highest X coordinate).
|
||||
*/
|
||||
template<class RawShape>
|
||||
inline TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
|
||||
{
|
||||
|
||||
// find min x and min y vertex
|
||||
auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh),
|
||||
__nfp::_vsort<RawShape>);
|
||||
|
||||
return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vertex of the polygon that is at the highest values (top) in the Y
|
||||
* axis and if there are more than one vertices on the same Y coordinate than
|
||||
* the result will be the rightmost (with the lowest X coordinate).
|
||||
*/
|
||||
template<class RawShape>
|
||||
TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
|
||||
{
|
||||
|
||||
// find max x and max y vertex
|
||||
auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh),
|
||||
__nfp::_vsort<RawShape>);
|
||||
|
||||
return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,55 +129,11 @@ static Shapes<RawShape> merge(const Shapes<RawShape>& shc,
|
||||
* reference will be always the same for the same polygon.
|
||||
*/
|
||||
template<class RawShape>
|
||||
inline static TPoint<RawShape> referenceVertex(const RawShape& sh)
|
||||
inline TPoint<RawShape> referenceVertex(const RawShape& sh)
|
||||
{
|
||||
return rightmostUpVertex(sh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vertex of the polygon that is at the lowest values (bottom) in the Y
|
||||
* axis and if there are more than one vertices on the same Y coordinate than
|
||||
* the result will be the leftmost (with the highest X coordinate).
|
||||
*/
|
||||
template<class RawShape>
|
||||
static TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
|
||||
{
|
||||
|
||||
// find min x and min y vertex
|
||||
auto it = std::min_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh),
|
||||
_vsort<RawShape>);
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vertex of the polygon that is at the highest values (top) in the Y
|
||||
* axis and if there are more than one vertices on the same Y coordinate than
|
||||
* the result will be the rightmost (with the lowest X coordinate).
|
||||
*/
|
||||
template<class RawShape>
|
||||
static TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
|
||||
{
|
||||
|
||||
// find max x and max y vertex
|
||||
auto it = std::max_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh),
|
||||
_vsort<RawShape>);
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
using NfpResult = std::pair<RawShape, TPoint<RawShape>>;
|
||||
|
||||
/// Helper function to get the NFP
|
||||
template<NfpLevel nfptype, class RawShape>
|
||||
static NfpResult<RawShape> noFitPolygon(const RawShape& sh,
|
||||
const RawShape& other)
|
||||
{
|
||||
NfpImpl<RawShape, nfptype> nfp;
|
||||
return nfp(sh, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "trivial" Cuninghame-Green implementation of NFP for convex polygons.
|
||||
*
|
||||
@ -139,11 +153,11 @@ static NfpResult<RawShape> noFitPolygon(const RawShape& sh,
|
||||
*
|
||||
*/
|
||||
template<class RawShape>
|
||||
static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
||||
inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
||||
const RawShape& other)
|
||||
{
|
||||
using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>;
|
||||
using sl = ShapeLike;
|
||||
namespace sl = shapelike;
|
||||
|
||||
RawShape rsh; // Final nfp placeholder
|
||||
Vertex top_nfp;
|
||||
@ -187,7 +201,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
||||
sl::addVertex(rsh, edgelist.front().second());
|
||||
|
||||
// Sorting function for the nfp reference vertex search
|
||||
auto& cmp = _vsort<RawShape>;
|
||||
auto& cmp = __nfp::_vsort<RawShape>;
|
||||
|
||||
// the reference (rightmost top) vertex so far
|
||||
top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp );
|
||||
@ -214,7 +228,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
|
||||
NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
|
||||
const RawShape& cother)
|
||||
{
|
||||
|
||||
@ -233,7 +247,7 @@ static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
|
||||
using Vertex = TPoint<RawShape>;
|
||||
using Coord = TCoord<Vertex>;
|
||||
using Edge = _Segment<Vertex>;
|
||||
using sl = ShapeLike;
|
||||
namespace sl = shapelike;
|
||||
using std::signbit;
|
||||
using std::sort;
|
||||
using std::vector;
|
||||
@ -528,27 +542,16 @@ struct NfpImpl {
|
||||
}
|
||||
};
|
||||
|
||||
template<class RawShape> struct MaxNfpLevel {
|
||||
static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// Do not specialize this...
|
||||
template<class RawShape>
|
||||
static inline bool _vsort(const TPoint<RawShape>& v1,
|
||||
const TPoint<RawShape>& v2)
|
||||
/// Helper function to get the NFP
|
||||
template<NfpLevel nfptype, class RawShape>
|
||||
inline NfpResult<RawShape> noFitPolygon(const RawShape& sh,
|
||||
const RawShape& other)
|
||||
{
|
||||
using Coord = TCoord<TPoint<RawShape>>;
|
||||
Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2);
|
||||
auto diff = y1 - y2;
|
||||
if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon())
|
||||
return x1 < x2;
|
||||
|
||||
return diff < 0;
|
||||
NfpImpl<RawShape, nfptype> nfps;
|
||||
return nfps(sh, other);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,12 @@
|
||||
#include <functional>
|
||||
|
||||
#include "geometry_traits.hpp"
|
||||
#include "optimizer.hpp"
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
namespace sl = shapelike;
|
||||
namespace pl = pointlike;
|
||||
|
||||
/**
|
||||
* \brief An item to be placed on a bin.
|
||||
*
|
||||
@ -28,7 +30,8 @@ class _Item {
|
||||
using Coord = TCoord<TPoint<RawShape>>;
|
||||
using Vertex = TPoint<RawShape>;
|
||||
using Box = _Box<Vertex>;
|
||||
using sl = ShapeLike;
|
||||
|
||||
using VertexConstIterator = typename TContour<RawShape>::const_iterator;
|
||||
|
||||
// The original shape that gets encapsulated.
|
||||
RawShape sh_;
|
||||
@ -38,7 +41,7 @@ class _Item {
|
||||
Radians rotation_;
|
||||
Coord offset_distance_;
|
||||
|
||||
// Info about whether the tranformations will have to take place
|
||||
// Info about whether the transformations will have to take place
|
||||
// This is needed because if floating point is used, it is hard to say
|
||||
// that a zero angle is not a rotation because of testing for equality.
|
||||
bool has_rotation_ = false, has_translation_ = false, has_offset_ = false;
|
||||
@ -58,12 +61,12 @@ class _Item {
|
||||
};
|
||||
|
||||
mutable Convexity convexity_ = Convexity::UNCHECKED;
|
||||
mutable TVertexConstIterator<RawShape> rmt_; // rightmost top vertex
|
||||
mutable TVertexConstIterator<RawShape> lmb_; // leftmost bottom vertex
|
||||
mutable VertexConstIterator rmt_; // rightmost top vertex
|
||||
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:
|
||||
@ -80,7 +83,7 @@ public:
|
||||
* supports. Giving out a non const iterator would make it impossible to
|
||||
* perform correct cache invalidation.
|
||||
*/
|
||||
using Iterator = TVertexConstIterator<RawShape>;
|
||||
using Iterator = VertexConstIterator;
|
||||
|
||||
/**
|
||||
* @brief Get the orientation of the polygon.
|
||||
@ -109,7 +112,7 @@ public:
|
||||
explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {}
|
||||
|
||||
/**
|
||||
* @brief Create an item from an initilizer list.
|
||||
* @brief Create an item from an initializer list.
|
||||
* @param il The initializer list of vertices.
|
||||
*/
|
||||
inline _Item(const std::initializer_list< Vertex >& il):
|
||||
@ -159,7 +162,7 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a copy of an outer vertex whithin the carried shape.
|
||||
* @brief Get a copy of an outer vertex within the carried shape.
|
||||
*
|
||||
* Note that the vertex considered here is taken from the original shape
|
||||
* that this item is constructed from. This means that no transformation is
|
||||
@ -244,7 +247,7 @@ public:
|
||||
* @param p
|
||||
* @return
|
||||
*/
|
||||
inline bool isPointInside(const Vertex& p) const
|
||||
inline bool isInside(const Vertex& p) const
|
||||
{
|
||||
return sl::isInside(p, transformedShape());
|
||||
}
|
||||
@ -307,7 +310,7 @@ public:
|
||||
{
|
||||
if(translation_ != tr) {
|
||||
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
|
||||
bb_cache_.valid = false;
|
||||
//bb_cache_.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,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 {
|
||||
@ -438,7 +447,7 @@ public:
|
||||
inline _Rectangle(Unit width, Unit height,
|
||||
// disable this ctor if o != CLOCKWISE
|
||||
enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
|
||||
_Item<RawShape>( ShapeLike::create<RawShape>( {
|
||||
_Item<RawShape>( sl::create<RawShape>( {
|
||||
{0, 0},
|
||||
{0, height},
|
||||
{width, height},
|
||||
@ -452,7 +461,7 @@ public:
|
||||
inline _Rectangle(Unit width, Unit height,
|
||||
// disable this ctor if o != COUNTER_CLOCKWISE
|
||||
enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
|
||||
_Item<RawShape>( ShapeLike::create<RawShape>( {
|
||||
_Item<RawShape>( sl::create<RawShape>( {
|
||||
{0, 0},
|
||||
{width, 0},
|
||||
{width, height},
|
||||
@ -473,18 +482,38 @@ public:
|
||||
|
||||
template<class RawShape>
|
||||
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
|
||||
return ShapeLike::isInside<RawShape>(boundingBox(), box);
|
||||
return sl::isInside<RawShape>(boundingBox(), box);
|
||||
}
|
||||
|
||||
template<class RawShape> inline bool
|
||||
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
|
||||
return ShapeLike::isInside<RawShape>(transformedShape(), circ);
|
||||
return sl::isInside<RawShape>(transformedShape(), circ);
|
||||
}
|
||||
|
||||
|
||||
template<class I> using _ItemRef = std::reference_wrapper<I>;
|
||||
template<class I> using _ItemGroup = std::vector<_ItemRef<I>>;
|
||||
|
||||
template<class Iterator>
|
||||
struct ConstItemRange {
|
||||
Iterator from;
|
||||
Iterator to;
|
||||
bool valid = false;
|
||||
|
||||
ConstItemRange() = default;
|
||||
ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {}
|
||||
};
|
||||
|
||||
template<class Container>
|
||||
inline ConstItemRange<typename Container::const_iterator>
|
||||
rem(typename Container::const_iterator it, const Container& cont) {
|
||||
return {std::next(it), cont.end()};
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A wrapper interface (trait) class for any placement strategy provider.
|
||||
*
|
||||
* If a client want's to use its own placement algorithm, all it has to do is to
|
||||
* If a client wants to use its own placement algorithm, all it has to do is to
|
||||
* specialize this class template and define all the ten methods it has. It can
|
||||
* use the strategies::PlacerBoilerplace class for creating a new placement
|
||||
* strategy where only the constructor and the trypack method has to be provided
|
||||
@ -515,8 +544,9 @@ public:
|
||||
*/
|
||||
using PackResult = typename PlacementStrategy::PackResult;
|
||||
|
||||
using ItemRef = std::reference_wrapper<Item>;
|
||||
using ItemGroup = std::vector<ItemRef>;
|
||||
using ItemRef = _ItemRef<Item>;
|
||||
using ItemGroup = _ItemGroup<Item>;
|
||||
using DefaultIterator = typename ItemGroup::const_iterator;
|
||||
|
||||
/**
|
||||
* @brief Constructor taking the bin and an optional configuration.
|
||||
@ -536,29 +566,32 @@ public:
|
||||
* Note that it depends on the particular placer implementation how it
|
||||
* reacts to config changes in the middle of a calculation.
|
||||
*
|
||||
* @param config The configuration object defined by the placement startegy.
|
||||
* @param config The configuration object defined by the placement strategy.
|
||||
*/
|
||||
inline void configure(const Config& config) { impl_.configure(config); }
|
||||
|
||||
/**
|
||||
* @brief A method that tries to pack an item and returns an object
|
||||
* describing the pack result.
|
||||
* Try to pack an item with a result object that contains the packing
|
||||
* information for later accepting it.
|
||||
*
|
||||
* The result can be casted to bool and used as an argument to the accept
|
||||
* method to accept a succesfully packed item. This way the next packing
|
||||
* will consider the accepted item as well. The PackResult should carry the
|
||||
* transformation info so that if the tried item is later modified or tried
|
||||
* multiple times, the result object should set it to the originally
|
||||
* determied position. An implementation can be found in the
|
||||
* strategies::PlacerBoilerplate::PackResult class.
|
||||
*
|
||||
* @param item Ithe item to be packed.
|
||||
* @return The PackResult object that can be implicitly casted to bool.
|
||||
* \param item_store A container of items that are intended to be packed
|
||||
* later. Can be used by the placer to switch tactics. When it's knows that
|
||||
* many items will come a greedy strategy may not be the best.
|
||||
* \param from The iterator to the item from which the packing should start,
|
||||
* including the pointed item
|
||||
* \param count How many items should be packed. If the value is 1, than
|
||||
* just the item pointed to by "from" argument should be packed.
|
||||
*/
|
||||
inline PackResult trypack(Item& item) { return impl_.trypack(item); }
|
||||
template<class Iter = DefaultIterator>
|
||||
inline PackResult trypack(
|
||||
Item& item,
|
||||
const ConstItemRange<Iter>& remaining = ConstItemRange<Iter>())
|
||||
{
|
||||
return impl_.trypack(item, remaining);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A method to accept a previously tried item.
|
||||
* @brief A method to accept a previously tried item (or items).
|
||||
*
|
||||
* If the pack result is a failure the method should ignore it.
|
||||
* @param r The result of a previous trypack call.
|
||||
@ -566,19 +599,25 @@ public:
|
||||
inline void accept(PackResult& r) { impl_.accept(r); }
|
||||
|
||||
/**
|
||||
* @brief pack Try to pack an item and immediately accept it on success.
|
||||
* @brief pack Try to pack and immediately accept it on success.
|
||||
*
|
||||
* A default implementation would be to call
|
||||
* { auto&& r = trypack(item); accept(r); return r; } but we should let the
|
||||
* { auto&& r = trypack(...); accept(r); return r; } but we should let the
|
||||
* implementor of the placement strategy to harvest any optimizations from
|
||||
* the absence of an intermadiate step. The above version can still be used
|
||||
* the absence of an intermediate step. The above version can still be used
|
||||
* in the implementation.
|
||||
*
|
||||
* @param item The item to pack.
|
||||
* @return Returns true if the item was packed or false if it could not be
|
||||
* packed.
|
||||
*/
|
||||
inline bool pack(Item& item) { return impl_.pack(item); }
|
||||
template<class Range = ConstItemRange<DefaultIterator>>
|
||||
inline bool pack(
|
||||
Item& item,
|
||||
const Range& remaining = Range())
|
||||
{
|
||||
return impl_.pack(item, remaining);
|
||||
}
|
||||
|
||||
/// Unpack the last element (remove it from the list of packed items).
|
||||
inline void unpackLast() { impl_.unpackLast(); }
|
||||
@ -597,13 +636,6 @@ public:
|
||||
|
||||
inline double filledArea() const { return impl_.filledArea(); }
|
||||
|
||||
#ifndef NDEBUG
|
||||
inline auto getDebugItems() -> decltype(impl_.debug_items_)&
|
||||
{
|
||||
return impl_.debug_items_;
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
// The progress function will be called with the number of placed items
|
||||
@ -628,15 +660,15 @@ public:
|
||||
* Note that it depends on the particular placer implementation how it
|
||||
* reacts to config changes in the middle of a calculation.
|
||||
*
|
||||
* @param config The configuration object defined by the selection startegy.
|
||||
* @param config The configuration object defined by the selection strategy.
|
||||
*/
|
||||
inline void configure(const Config& config) {
|
||||
impl_.configure(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A function callback which should be called whenewer an item or
|
||||
* a group of items where succesfully packed.
|
||||
* @brief A function callback which should be called whenever an item or
|
||||
* a group of items where successfully packed.
|
||||
* @param fn A function callback object taking one unsigned integer as the
|
||||
* number of the remaining items to pack.
|
||||
*/
|
||||
@ -649,7 +681,7 @@ public:
|
||||
* placer compatible with the PlacementStrategyLike interface.
|
||||
*
|
||||
* \param first, last The first and last iterator if the input sequence. It
|
||||
* can be only an iterator of a type converitible to Item.
|
||||
* can be only an iterator of a type convertible to Item.
|
||||
* \param bin. The shape of the bin. It has to be supported by the placement
|
||||
* strategy.
|
||||
* \param An optional config object for the placer.
|
||||
@ -681,7 +713,7 @@ public:
|
||||
/**
|
||||
* @brief Get the items for a particular bin.
|
||||
* @param binIndex The index of the requested bin.
|
||||
* @return Returns a list of allitems packed into the requested bin.
|
||||
* @return Returns a list of all items packed into the requested bin.
|
||||
*/
|
||||
inline ItemGroup itemsForBin(size_t binIndex) {
|
||||
return impl_.itemsForBin(binIndex);
|
||||
@ -723,15 +755,14 @@ using _IndexedPackGroup = std::vector<
|
||||
>;
|
||||
|
||||
/**
|
||||
* The Arranger is the frontend class for the binpack2d library. It takes the
|
||||
* The Arranger is the front-end class for the libnest2d library. It takes the
|
||||
* input items and outputs the items with the proper transformations to be
|
||||
* inside the provided bin.
|
||||
*/
|
||||
template<class PlacementStrategy, class SelectionStrategy >
|
||||
class Arranger {
|
||||
class Nester {
|
||||
using TSel = SelectionStrategyLike<SelectionStrategy>;
|
||||
TSel selector_;
|
||||
bool use_min_bb_rotation_ = false;
|
||||
public:
|
||||
using Item = typename PlacementStrategy::Item;
|
||||
using ItemRef = std::reference_wrapper<Item>;
|
||||
@ -769,7 +800,7 @@ public:
|
||||
template<class TBinType = BinType,
|
||||
class PConf = PlacementConfig,
|
||||
class SConf = SelectionConfig>
|
||||
Arranger( TBinType&& bin,
|
||||
Nester( TBinType&& bin,
|
||||
Unit min_obj_distance = 0,
|
||||
PConf&& pconfig = PConf(),
|
||||
SConf&& sconfig = SConf()):
|
||||
@ -802,9 +833,9 @@ public:
|
||||
* the selection algorithm.
|
||||
*/
|
||||
template<class TIterator>
|
||||
inline PackGroup arrange(TIterator from, TIterator to)
|
||||
inline PackGroup execute(TIterator from, TIterator to)
|
||||
{
|
||||
return _arrange(from, to);
|
||||
return _execute(from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -815,20 +846,20 @@ public:
|
||||
* input sequence size.
|
||||
*/
|
||||
template<class TIterator>
|
||||
inline IndexedPackGroup arrangeIndexed(TIterator from, TIterator to)
|
||||
inline IndexedPackGroup executeIndexed(TIterator from, TIterator to)
|
||||
{
|
||||
return _arrangeIndexed(from, to);
|
||||
return _executeIndexed(from, to);
|
||||
}
|
||||
|
||||
/// Shorthand to normal arrange method.
|
||||
template<class TIterator>
|
||||
inline PackGroup operator() (TIterator from, TIterator to)
|
||||
{
|
||||
return _arrange(from, to);
|
||||
return _execute(from, to);
|
||||
}
|
||||
|
||||
/// Set a progress indicatior function object for the selector.
|
||||
inline Arranger& progressIndicator(ProgressFunction func)
|
||||
/// Set a progress indicator function object for the selector.
|
||||
inline Nester& progressIndicator(ProgressFunction func)
|
||||
{
|
||||
selector_.progressIndicator(func); return *this;
|
||||
}
|
||||
@ -842,24 +873,20 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline Arranger& useMinimumBoundigBoxRotation(bool s = true) {
|
||||
use_min_bb_rotation_ = s; return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<class TIterator,
|
||||
class IT = remove_cvref_t<typename TIterator::value_type>,
|
||||
|
||||
// This funtion will be used only if the iterators are pointing to
|
||||
// a type compatible with the binpack2d::_Item template.
|
||||
// This function will be used only if the iterators are pointing to
|
||||
// a type compatible with the libnets2d::_Item template.
|
||||
// This way we can use references to input elements as they will
|
||||
// have to exist for the lifetime of this call.
|
||||
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
|
||||
>
|
||||
inline PackGroup _arrange(TIterator from, TIterator to, bool = false)
|
||||
inline PackGroup _execute(TIterator from, TIterator to, bool = false)
|
||||
{
|
||||
__arrange(from, to);
|
||||
__execute(from, to);
|
||||
return lastResult();
|
||||
}
|
||||
|
||||
@ -867,28 +894,28 @@ private:
|
||||
class IT = remove_cvref_t<typename TIterator::value_type>,
|
||||
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
|
||||
>
|
||||
inline PackGroup _arrange(TIterator from, TIterator to, int = false)
|
||||
inline PackGroup _execute(TIterator from, TIterator to, int = false)
|
||||
{
|
||||
item_cache_ = {from, to};
|
||||
|
||||
__arrange(item_cache_.begin(), item_cache_.end());
|
||||
__execute(item_cache_.begin(), item_cache_.end());
|
||||
return lastResult();
|
||||
}
|
||||
|
||||
template<class TIterator,
|
||||
class IT = remove_cvref_t<typename TIterator::value_type>,
|
||||
|
||||
// This funtion will be used only if the iterators are pointing to
|
||||
// a type compatible with the binpack2d::_Item template.
|
||||
// This function will be used only if the iterators are pointing to
|
||||
// a type compatible with the libnest2d::_Item template.
|
||||
// This way we can use references to input elements as they will
|
||||
// have to exist for the lifetime of this call.
|
||||
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
|
||||
>
|
||||
inline IndexedPackGroup _arrangeIndexed(TIterator from,
|
||||
inline IndexedPackGroup _executeIndexed(TIterator from,
|
||||
TIterator to,
|
||||
bool = false)
|
||||
{
|
||||
__arrange(from, to);
|
||||
__execute(from, to);
|
||||
return createIndexedPackGroup(from, to, selector_);
|
||||
}
|
||||
|
||||
@ -896,12 +923,12 @@ private:
|
||||
class IT = remove_cvref_t<typename TIterator::value_type>,
|
||||
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
|
||||
>
|
||||
inline IndexedPackGroup _arrangeIndexed(TIterator from,
|
||||
inline IndexedPackGroup _executeIndexed(TIterator from,
|
||||
TIterator to,
|
||||
int = false)
|
||||
{
|
||||
item_cache_ = {from, to};
|
||||
__arrange(item_cache_.begin(), item_cache_.end());
|
||||
__execute(item_cache_.begin(), item_cache_.end());
|
||||
return createIndexedPackGroup(from, to, selector_);
|
||||
}
|
||||
|
||||
@ -933,37 +960,12 @@ private:
|
||||
return pg;
|
||||
}
|
||||
|
||||
Radians findBestRotation(Item& item) {
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.absolute_score_difference = 0.01;
|
||||
stopcr.max_iterations = 10000;
|
||||
opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr);
|
||||
|
||||
auto orig_rot = item.rotation();
|
||||
|
||||
auto result = solver.optimize_min([&item, &orig_rot](Radians rot){
|
||||
item.rotation(orig_rot + rot);
|
||||
auto bb = item.boundingBox();
|
||||
return std::sqrt(bb.height()*bb.width());
|
||||
}, opt::initvals(Radians(0)), opt::bound<Radians>(-Pi/2, Pi/2));
|
||||
|
||||
item.rotation(orig_rot);
|
||||
|
||||
return std::get<0>(result.optimum);
|
||||
}
|
||||
|
||||
template<class TIter> inline void __arrange(TIter from, TIter to)
|
||||
template<class TIter> inline void __execute(TIter from, TIter to)
|
||||
{
|
||||
if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) {
|
||||
item.addOffset(static_cast<Unit>(std::ceil(min_obj_distance_/2.0)));
|
||||
});
|
||||
|
||||
if(use_min_bb_rotation_)
|
||||
std::for_each(from, to, [this](Item& item){
|
||||
Radians rot = findBestRotation(item);
|
||||
item.rotate(rot);
|
||||
});
|
||||
|
||||
selector_.template packItems<PlacementStrategy>(
|
||||
from, to, bin_, pconfig_);
|
||||
|
||||
|
@ -67,11 +67,11 @@ class metaloop {
|
||||
// need to wrap that in a type (see metaloop::Int).
|
||||
|
||||
/*
|
||||
* A helper alias to create integer values wrapped as a type. It is nessecary
|
||||
* A helper alias to create integer values wrapped as a type. It is necessary
|
||||
* because a non type template parameter (such as int) would be prohibited in
|
||||
* a partial specialization. Also for the same reason we have to use a class
|
||||
* _Metaloop instead of a simple function as a functor. A function cannot be
|
||||
* partially specialized in a way that is neccesary for this trick.
|
||||
* partially specialized in a way that is necessary for this trick.
|
||||
*/
|
||||
template<int N> using Int = std::integral_constant<int, N>;
|
||||
|
||||
@ -88,7 +88,7 @@ public:
|
||||
// It takes the real functor that can be specified in-place but only
|
||||
// with C++14 because the second parameter's type will depend on the
|
||||
// type of the parameter pack element that is processed. In C++14 we can
|
||||
// specify this second parameter type as auto in the lamda parameter list.
|
||||
// specify this second parameter type as auto in the lambda parameter list.
|
||||
inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
|
||||
|
||||
template<class T> void operator ()(T&& pack_element) {
|
||||
@ -146,7 +146,7 @@ public:
|
||||
* version of run is called which does not call itself anymore.
|
||||
*
|
||||
* If you are utterly annoyed, at least you have learned a super crazy
|
||||
* functional metaprogramming pattern.
|
||||
* functional meta-programming pattern.
|
||||
*/
|
||||
template<class...Args>
|
||||
using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;
|
||||
|
@ -102,6 +102,9 @@ struct StopCriteria {
|
||||
/// If the relative value difference between two scores.
|
||||
double relative_score_difference = std::nan("");
|
||||
|
||||
/// Stop if this value or better is found.
|
||||
double stop_score = std::nan("");
|
||||
|
||||
unsigned max_iterations = 0;
|
||||
};
|
||||
|
||||
|
@ -142,10 +142,12 @@ protected:
|
||||
default: ;
|
||||
}
|
||||
|
||||
auto abs_diff = stopcr_.absolute_score_difference;
|
||||
auto rel_diff = stopcr_.relative_score_difference;
|
||||
double abs_diff = stopcr_.absolute_score_difference;
|
||||
double rel_diff = stopcr_.relative_score_difference;
|
||||
double stopval = stopcr_.stop_score;
|
||||
if(!std::isnan(abs_diff)) opt_.set_ftol_abs(abs_diff);
|
||||
if(!std::isnan(rel_diff)) opt_.set_ftol_rel(rel_diff);
|
||||
if(!std::isnan(stopval)) opt_.set_stopval(stopval);
|
||||
|
||||
if(this->stopcr_.max_iterations > 0)
|
||||
opt_.set_maxeval(this->stopcr_.max_iterations );
|
||||
|
@ -5,11 +5,26 @@
|
||||
|
||||
#include "placer_boilerplate.hpp"
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
namespace libnest2d { namespace placers {
|
||||
|
||||
template<class T, class = T> struct Epsilon {};
|
||||
|
||||
template<class T>
|
||||
struct Epsilon<T, enable_if_t<std::is_integral<T>::value, T> > {
|
||||
static const T Value = 1;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct Epsilon<T, enable_if_t<std::is_floating_point<T>::value, T> > {
|
||||
static const T Value = 1e-3;
|
||||
};
|
||||
|
||||
template<class RawShape>
|
||||
struct BLConfig {
|
||||
TCoord<TPoint<RawShape>> min_obj_distance = 0;
|
||||
DECLARE_MAIN_TYPES(RawShape);
|
||||
|
||||
Coord min_obj_distance = 0;
|
||||
Coord epsilon = Epsilon<Coord>::Value;
|
||||
bool allow_rotations = false;
|
||||
};
|
||||
|
||||
@ -27,9 +42,13 @@ public:
|
||||
|
||||
explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {}
|
||||
|
||||
PackResult trypack(Item& item) {
|
||||
template<class Range = ConstItemRange<typename Base::DefaultIter>>
|
||||
PackResult trypack(Item& item,
|
||||
const Range& = Range())
|
||||
{
|
||||
auto r = _trypack(item);
|
||||
if(!r && Base::config_.allow_rotations) {
|
||||
|
||||
item.rotate(Degrees(90));
|
||||
r =_trypack(item);
|
||||
}
|
||||
@ -65,20 +84,21 @@ protected:
|
||||
setInitialPosition(item);
|
||||
|
||||
Unit d = availableSpaceDown(item);
|
||||
bool can_move = d > 1 /*std::numeric_limits<Unit>::epsilon()*/;
|
||||
auto eps = config_.epsilon;
|
||||
bool can_move = d > eps;
|
||||
bool can_be_packed = can_move;
|
||||
bool left = true;
|
||||
|
||||
while(can_move) {
|
||||
if(left) { // write previous down move and go down
|
||||
item.translate({0, -d+1});
|
||||
item.translate({0, -d+eps});
|
||||
d = availableSpaceLeft(item);
|
||||
can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/;
|
||||
can_move = d > eps;
|
||||
left = false;
|
||||
} else { // write previous left move and go down
|
||||
item.translate({-d+1, 0});
|
||||
item.translate({-d+eps, 0});
|
||||
d = availableSpaceDown(item);
|
||||
can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/;
|
||||
can_move = d > eps;
|
||||
left = true;
|
||||
}
|
||||
}
|
||||
@ -112,10 +132,10 @@ protected:
|
||||
const RawShape& scanpoly)
|
||||
{
|
||||
auto tsh = other.transformedShape();
|
||||
return ( ShapeLike::intersects(tsh, scanpoly) ||
|
||||
ShapeLike::isInside(tsh, scanpoly) ) &&
|
||||
( !ShapeLike::intersects(tsh, item.rawShape()) &&
|
||||
!ShapeLike::isInside(tsh, item.rawShape()) );
|
||||
return ( sl::intersects(tsh, scanpoly) ||
|
||||
sl::isInside(tsh, scanpoly) ) &&
|
||||
( !sl::intersects(tsh, item.rawShape()) &&
|
||||
!sl::isInside(tsh, item.rawShape()) );
|
||||
}
|
||||
|
||||
template<class C = Coord>
|
||||
@ -126,25 +146,25 @@ protected:
|
||||
{
|
||||
auto tsh = other.transformedShape();
|
||||
|
||||
bool inters_scanpoly = ShapeLike::intersects(tsh, scanpoly) &&
|
||||
!ShapeLike::touches(tsh, scanpoly);
|
||||
bool inters_item = ShapeLike::intersects(tsh, item.rawShape()) &&
|
||||
!ShapeLike::touches(tsh, item.rawShape());
|
||||
bool inters_scanpoly = sl::intersects(tsh, scanpoly) &&
|
||||
!sl::touches(tsh, scanpoly);
|
||||
bool inters_item = sl::intersects(tsh, item.rawShape()) &&
|
||||
!sl::touches(tsh, item.rawShape());
|
||||
|
||||
return ( inters_scanpoly ||
|
||||
ShapeLike::isInside(tsh, scanpoly)) &&
|
||||
sl::isInside(tsh, scanpoly)) &&
|
||||
( !inters_item &&
|
||||
!ShapeLike::isInside(tsh, item.rawShape())
|
||||
!sl::isInside(tsh, item.rawShape())
|
||||
);
|
||||
}
|
||||
|
||||
Container itemsInTheWayOf(const Item& item, const Dir dir) {
|
||||
ItemGroup itemsInTheWayOf(const Item& item, const Dir dir) {
|
||||
// Get the left or down polygon, that has the same area as the shadow
|
||||
// of input item reflected to the left or downwards
|
||||
auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) :
|
||||
downPoly(item);
|
||||
|
||||
Container ret; // packed items 'in the way' of item
|
||||
ItemGroup ret; // packed items 'in the way' of item
|
||||
ret.reserve(items_.size());
|
||||
|
||||
// Predicate to find items that are 'in the way' for left (down) move
|
||||
@ -173,18 +193,18 @@ protected:
|
||||
|
||||
if(dir == Dir::LEFT) {
|
||||
getCoord = [](const Vertex& v) { return getX(v); };
|
||||
availableDistance = PointLike::horizontalDistance<Vertex>;
|
||||
availableDistance = pointlike::horizontalDistance<Vertex>;
|
||||
availableDistanceSV = [](const Segment& s, const Vertex& v) {
|
||||
auto ret = PointLike::horizontalDistance<Vertex>(v, s);
|
||||
auto ret = pointlike::horizontalDistance<Vertex>(v, s);
|
||||
if(ret.second) ret.first = -ret.first;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
else {
|
||||
getCoord = [](const Vertex& v) { return getY(v); };
|
||||
availableDistance = PointLike::verticalDistance<Vertex>;
|
||||
availableDistance = pointlike::verticalDistance<Vertex>;
|
||||
availableDistanceSV = [](const Segment& s, const Vertex& v) {
|
||||
auto ret = PointLike::verticalDistance<Vertex>(v, s);
|
||||
auto ret = pointlike::verticalDistance<Vertex>(v, s);
|
||||
if(ret.second) ret.first = -ret.first;
|
||||
return ret;
|
||||
};
|
||||
@ -214,9 +234,9 @@ protected:
|
||||
assert(pleft.vertexCount() > 0);
|
||||
|
||||
auto trpleft = pleft.transformedShape();
|
||||
auto first = ShapeLike::begin(trpleft);
|
||||
auto first = sl::begin(trpleft);
|
||||
auto next = first + 1;
|
||||
auto endit = ShapeLike::end(trpleft);
|
||||
auto endit = sl::end(trpleft);
|
||||
|
||||
while(next != endit) {
|
||||
Segment seg(*(first++), *(next++));
|
||||
@ -340,16 +360,16 @@ protected:
|
||||
|
||||
// reserve for all vertices plus 2 for the left horizontal wall, 2 for
|
||||
// the additional vertices for maintaning min object distance
|
||||
ShapeLike::reserve(rsh, finish-start+4);
|
||||
sl::reserve(rsh, finish-start+4);
|
||||
|
||||
/*auto addOthers = [&rsh, finish, start, &item](){
|
||||
for(size_t i = start+1; i < finish; i++)
|
||||
ShapeLike::addVertex(rsh, item.vertex(i));
|
||||
sl::addVertex(rsh, item.vertex(i));
|
||||
};*/
|
||||
|
||||
auto reverseAddOthers = [&rsh, finish, start, &item](){
|
||||
for(auto i = finish-1; i > start; i--)
|
||||
ShapeLike::addVertex(rsh, item.vertex(
|
||||
sl::addVertex(rsh, item.vertex(
|
||||
static_cast<unsigned long>(i)));
|
||||
};
|
||||
|
||||
@ -361,25 +381,25 @@ protected:
|
||||
|
||||
// Clockwise polygon construction
|
||||
|
||||
ShapeLike::addVertex(rsh, topleft_vertex);
|
||||
sl::addVertex(rsh, topleft_vertex);
|
||||
|
||||
if(dir == Dir::LEFT) reverseAddOthers();
|
||||
else {
|
||||
ShapeLike::addVertex(rsh, getX(topleft_vertex), 0);
|
||||
ShapeLike::addVertex(rsh, getX(bottomleft_vertex), 0);
|
||||
sl::addVertex(rsh, getX(topleft_vertex), 0);
|
||||
sl::addVertex(rsh, getX(bottomleft_vertex), 0);
|
||||
}
|
||||
|
||||
ShapeLike::addVertex(rsh, bottomleft_vertex);
|
||||
sl::addVertex(rsh, bottomleft_vertex);
|
||||
|
||||
if(dir == Dir::LEFT) {
|
||||
ShapeLike::addVertex(rsh, 0, getY(bottomleft_vertex));
|
||||
ShapeLike::addVertex(rsh, 0, getY(topleft_vertex));
|
||||
sl::addVertex(rsh, 0, getY(bottomleft_vertex));
|
||||
sl::addVertex(rsh, 0, getY(topleft_vertex));
|
||||
}
|
||||
else reverseAddOthers();
|
||||
|
||||
|
||||
// Close the polygon
|
||||
ShapeLike::addVertex(rsh, topleft_vertex);
|
||||
sl::addVertex(rsh, topleft_vertex);
|
||||
|
||||
return rsh;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,14 +3,11 @@
|
||||
|
||||
#include "../libnest2d.hpp"
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
namespace libnest2d { namespace placers {
|
||||
|
||||
struct EmptyConfig {};
|
||||
|
||||
template<class Subclass, class RawShape, class TBin,
|
||||
class Cfg = EmptyConfig,
|
||||
class Store = std::vector<std::reference_wrapper<_Item<RawShape>>>
|
||||
>
|
||||
template<class Subclass, class RawShape, class TBin, class Cfg = EmptyConfig>
|
||||
class PlacerBoilerplate {
|
||||
mutable bool farea_valid_ = false;
|
||||
mutable double farea_ = 0.0;
|
||||
@ -22,25 +19,30 @@ public:
|
||||
using Coord = TCoord<Vertex>;
|
||||
using Unit = Coord;
|
||||
using Config = Cfg;
|
||||
using Container = Store;
|
||||
using ItemGroup = _ItemGroup<Item>;
|
||||
using DefaultIter = typename ItemGroup::const_iterator;
|
||||
|
||||
class PackResult {
|
||||
Item *item_ptr_;
|
||||
Vertex move_;
|
||||
Radians rot_;
|
||||
double overfit_;
|
||||
friend class PlacerBoilerplate;
|
||||
friend Subclass;
|
||||
|
||||
PackResult(Item& item):
|
||||
item_ptr_(&item),
|
||||
move_(item.translation()),
|
||||
rot_(item.rotation()) {}
|
||||
PackResult(): item_ptr_(nullptr) {}
|
||||
|
||||
PackResult(double overfit = 1.0):
|
||||
item_ptr_(nullptr), overfit_(overfit) {}
|
||||
|
||||
public:
|
||||
operator bool() { return item_ptr_ != nullptr; }
|
||||
double overfit() const { return overfit_; }
|
||||
};
|
||||
|
||||
using ItemGroup = const Container&;
|
||||
|
||||
inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin)
|
||||
{
|
||||
items_.reserve(cap);
|
||||
@ -56,8 +58,10 @@ public:
|
||||
config_ = config;
|
||||
}
|
||||
|
||||
bool pack(Item& item) {
|
||||
auto&& r = static_cast<Subclass*>(this)->trypack(item);
|
||||
template<class Range = ConstItemRange<DefaultIter>>
|
||||
bool pack(Item& item,
|
||||
const Range& rem = Range()) {
|
||||
auto&& r = static_cast<Subclass*>(this)->trypack(item, rem);
|
||||
if(r) {
|
||||
items_.push_back(*(r.item_ptr_));
|
||||
farea_valid_ = false;
|
||||
@ -79,14 +83,11 @@ public:
|
||||
farea_valid_ = false;
|
||||
}
|
||||
|
||||
inline ItemGroup getItems() const { return items_; }
|
||||
inline const ItemGroup& getItems() const { return items_; }
|
||||
|
||||
inline void clearItems() {
|
||||
items_.clear();
|
||||
farea_valid_ = false;
|
||||
#ifndef NDEBUG
|
||||
debug_items_.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline double filledArea() const {
|
||||
@ -103,14 +104,10 @@ public:
|
||||
return farea_;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::vector<Item> debug_items_;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
BinType bin_;
|
||||
Container items_;
|
||||
ItemGroup items_;
|
||||
Cfg config_;
|
||||
};
|
||||
|
||||
@ -121,6 +118,7 @@ using Base::items_; \
|
||||
using Base::config_; \
|
||||
public: \
|
||||
using typename Base::Item; \
|
||||
using typename Base::ItemGroup; \
|
||||
using typename Base::BinType; \
|
||||
using typename Base::Config; \
|
||||
using typename Base::Vertex; \
|
||||
@ -128,7 +126,6 @@ using typename Base::Segment; \
|
||||
using typename Base::PackResult; \
|
||||
using typename Base::Coord; \
|
||||
using typename Base::Unit; \
|
||||
using typename Base::Container; \
|
||||
private:
|
||||
|
||||
}
|
||||
|
41
xs/src/libnest2d/libnest2d/rotfinder.hpp
Normal file
41
xs/src/libnest2d/libnest2d/rotfinder.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef ROTFINDER_HPP
|
||||
#define ROTFINDER_HPP
|
||||
|
||||
#include <libnest2d/libnest2d.hpp>
|
||||
#include <libnest2d/optimizer.hpp>
|
||||
#include <iterator>
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
template<class RawShape>
|
||||
Radians findBestRotation(_Item<RawShape>& item) {
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.absolute_score_difference = 0.01;
|
||||
stopcr.max_iterations = 10000;
|
||||
opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr);
|
||||
|
||||
auto orig_rot = item.rotation();
|
||||
|
||||
auto result = solver.optimize_min([&item, &orig_rot](Radians rot){
|
||||
item.rotation(orig_rot + rot);
|
||||
auto bb = item.boundingBox();
|
||||
return std::sqrt(bb.height()*bb.width());
|
||||
}, opt::initvals(Radians(0)), opt::bound<Radians>(-Pi/2, Pi/2));
|
||||
|
||||
item.rotation(orig_rot);
|
||||
|
||||
return std::get<0>(result.optimum);
|
||||
}
|
||||
|
||||
template<class Iterator>
|
||||
void findMinimumBoundingBoxRotations(Iterator from, Iterator to) {
|
||||
using V = typename std::iterator_traits<Iterator>::value_type;
|
||||
std::for_each(from, to, [](V& item){
|
||||
Radians rot = findBestRotation(item);
|
||||
item.rotate(rot);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // ROTFINDER_HPP
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "selection_boilerplate.hpp"
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
namespace libnest2d { namespace selections {
|
||||
|
||||
/**
|
||||
* Selection heuristic based on [López-Camacho]\
|
||||
@ -118,7 +118,7 @@ public:
|
||||
using Placer = PlacementStrategyLike<TPlacer>;
|
||||
using ItemList = std::list<ItemRef>;
|
||||
|
||||
const double bin_area = ShapeLike::area<RawShape>(bin);
|
||||
const double bin_area = sl::area(bin);
|
||||
const double w = bin_area * config_.waste_increment;
|
||||
|
||||
const double INITIAL_FILL_PROPORTION = config_.initial_fill_proportion;
|
||||
@ -227,10 +227,14 @@ public:
|
||||
bool ret = false;
|
||||
auto it = not_packed.begin();
|
||||
|
||||
auto pack = [&placer, ¬_packed](ItemListIt it) {
|
||||
return placer.pack(*it, rem(it, not_packed));
|
||||
};
|
||||
|
||||
while(it != not_packed.end() && !ret &&
|
||||
free_area - (item_area = it->get().area()) <= waste)
|
||||
{
|
||||
if(item_area <= free_area && placer.pack(*it) ) {
|
||||
if(item_area <= free_area && pack(it) ) {
|
||||
free_area -= item_area;
|
||||
filled_area = bin_area - free_area;
|
||||
ret = true;
|
||||
@ -270,6 +274,11 @@ public:
|
||||
auto it2 = it;
|
||||
|
||||
std::vector<TPair> wrong_pairs;
|
||||
using std::placeholders::_1;
|
||||
|
||||
auto trypack = [&placer, ¬_packed](ItemListIt it) {
|
||||
return placer.trypack(*it, rem(it, not_packed));
|
||||
};
|
||||
|
||||
while(it != endit && !ret &&
|
||||
free_area - (item_area = it->get().area()) -
|
||||
@ -278,7 +287,7 @@ public:
|
||||
if(item_area + smallestPiece(it, not_packed)->get().area() >
|
||||
free_area ) { it++; continue; }
|
||||
|
||||
auto pr = placer.trypack(*it);
|
||||
auto pr = trypack(it);
|
||||
|
||||
// First would fit
|
||||
it2 = not_packed.begin();
|
||||
@ -294,14 +303,14 @@ public:
|
||||
}
|
||||
|
||||
placer.accept(pr);
|
||||
auto pr2 = placer.trypack(*it2);
|
||||
auto pr2 = trypack(it2);
|
||||
if(!pr2) {
|
||||
placer.unpackLast(); // remove first
|
||||
if(try_reverse) {
|
||||
pr2 = placer.trypack(*it2);
|
||||
pr2 = trypack(it2);
|
||||
if(pr2) {
|
||||
placer.accept(pr2);
|
||||
auto pr12 = placer.trypack(*it);
|
||||
auto pr12 = trypack(it);
|
||||
if(pr12) {
|
||||
placer.accept(pr12);
|
||||
ret = true;
|
||||
@ -365,6 +374,14 @@ public:
|
||||
return it->get().area();
|
||||
};
|
||||
|
||||
auto trypack = [&placer, ¬_packed](ItemListIt it) {
|
||||
return placer.trypack(*it, rem(it, not_packed));
|
||||
};
|
||||
|
||||
auto pack = [&placer, ¬_packed](ItemListIt it) {
|
||||
return placer.pack(*it, rem(it, not_packed));
|
||||
};
|
||||
|
||||
while (it != endit && !ret) { // drill down 1st level
|
||||
|
||||
// We need to determine in each iteration the largest, second
|
||||
@ -394,7 +411,7 @@ public:
|
||||
it++; continue;
|
||||
}
|
||||
|
||||
auto pr = placer.trypack(*it);
|
||||
auto pr = trypack(it);
|
||||
|
||||
// Check for free area and try to pack the 1st item...
|
||||
if(!pr) { it++; continue; }
|
||||
@ -420,15 +437,15 @@ public:
|
||||
bool can_pack2 = false;
|
||||
|
||||
placer.accept(pr);
|
||||
auto pr2 = placer.trypack(*it2);
|
||||
auto pr2 = trypack(it2);
|
||||
auto pr12 = pr;
|
||||
if(!pr2) {
|
||||
placer.unpackLast(); // remove first
|
||||
if(try_reverse) {
|
||||
pr2 = placer.trypack(*it2);
|
||||
pr2 = trypack(it2);
|
||||
if(pr2) {
|
||||
placer.accept(pr2);
|
||||
pr12 = placer.trypack(*it);
|
||||
pr12 = trypack(it);
|
||||
if(pr12) can_pack2 = true;
|
||||
placer.unpackLast();
|
||||
}
|
||||
@ -463,7 +480,7 @@ public:
|
||||
if(a3_sum > free_area) { it3++; continue; }
|
||||
|
||||
placer.accept(pr12); placer.accept(pr2);
|
||||
bool can_pack3 = placer.pack(*it3);
|
||||
bool can_pack3 = pack(it3);
|
||||
|
||||
if(!can_pack3) {
|
||||
placer.unpackLast();
|
||||
@ -473,16 +490,16 @@ public:
|
||||
if(!can_pack3 && try_reverse) {
|
||||
|
||||
std::array<size_t, 3> indices = {0, 1, 2};
|
||||
std::array<ItemRef, 3>
|
||||
candidates = {*it, *it2, *it3};
|
||||
std::array<typename ItemList::iterator, 3>
|
||||
candidates = {it, it2, it3};
|
||||
|
||||
auto tryPack = [&placer, &candidates](
|
||||
auto tryPack = [&placer, &candidates, &pack](
|
||||
const decltype(indices)& idx)
|
||||
{
|
||||
std::array<bool, 3> packed = {false};
|
||||
|
||||
for(auto id : idx) packed.at(id) =
|
||||
placer.pack(candidates[id]);
|
||||
pack(candidates[id]);
|
||||
|
||||
bool check =
|
||||
std::all_of(packed.begin(),
|
||||
@ -536,7 +553,7 @@ public:
|
||||
{ auto it = store_.begin();
|
||||
while (it != store_.end()) {
|
||||
Placer p(bin); p.configure(pconfig);
|
||||
if(!p.pack(*it)) {
|
||||
if(!p.pack(*it, rem(it, store_))) {
|
||||
it = store_.erase(it);
|
||||
} else it++;
|
||||
}
|
||||
@ -551,11 +568,7 @@ public:
|
||||
{
|
||||
|
||||
packed_bins_[idx] = placer.getItems();
|
||||
#ifndef NDEBUG
|
||||
packed_bins_[idx].insert(packed_bins_[idx].end(),
|
||||
placer.getDebugItems().begin(),
|
||||
placer.getDebugItems().end());
|
||||
#endif
|
||||
|
||||
// TODO here should be a spinlock
|
||||
slock.lock();
|
||||
acounter -= packednum;
|
||||
@ -601,7 +614,7 @@ public:
|
||||
while(it != not_packed.end() &&
|
||||
filled_area < INITIAL_FILL_AREA)
|
||||
{
|
||||
if(placer.pack(*it)) {
|
||||
if(placer.pack(*it, rem(it, not_packed))) {
|
||||
filled_area += it->get().area();
|
||||
free_area = bin_area - filled_area;
|
||||
it = not_packed.erase(it);
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "selection_boilerplate.hpp"
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
namespace libnest2d { namespace selections {
|
||||
|
||||
template<class RawShape>
|
||||
class _FillerSelection: public SelectionBoilerplate<RawShape> {
|
||||
@ -56,18 +56,13 @@ public:
|
||||
|
||||
std::sort(store_.begin(), store_.end(), sortfunc);
|
||||
|
||||
// Container a = {store_[0], store_[1], store_[4], store_[5] };
|
||||
//// a.insert(a.end(), store_.end()-10, store_.end());
|
||||
// store_ = a;
|
||||
|
||||
PlacementStrategyLike<TPlacer> placer(bin);
|
||||
placer.configure(pconfig);
|
||||
|
||||
auto it = store_.begin();
|
||||
while(it != store_.end()) {
|
||||
if(!placer.pack(*it)) {
|
||||
if(!placer.pack(*it, {std::next(it), store_.end()})) {
|
||||
if(packed_bins_.back().empty()) ++it;
|
||||
// makeProgress(placer);
|
||||
placer.clearItems();
|
||||
packed_bins_.emplace_back();
|
||||
} else {
|
||||
@ -76,9 +71,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// if(was_packed) {
|
||||
// packed_bins_.push_back(placer.getItems());
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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> {
|
||||
@ -40,6 +40,7 @@ public:
|
||||
packed_bins_.clear();
|
||||
|
||||
std::vector<Placer> placers;
|
||||
placers.reserve(last-first);
|
||||
|
||||
std::copy(first, last, std::back_inserter(store_));
|
||||
|
||||
@ -66,21 +67,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& item : store_ ) {
|
||||
bool was_packed = false;
|
||||
while(!was_packed) {
|
||||
auto it = store_.begin();
|
||||
|
||||
for(size_t j = 0; j < placers.size() && !was_packed; j++) {
|
||||
if((was_packed = placers[j].pack(item)))
|
||||
makeProgress(placers[j], j);
|
||||
while(it != store_.end()) {
|
||||
bool was_packed = false;
|
||||
size_t j = 0;
|
||||
while(!was_packed) {
|
||||
for(; j < placers.size() && !was_packed; j++) {
|
||||
if((was_packed = placers[j].pack(*it, rem(it, store_) )))
|
||||
makeProgress(placers[j], j);
|
||||
}
|
||||
|
||||
if(!was_packed) {
|
||||
placers.emplace_back(bin);
|
||||
placers.back().configure(pconfig);
|
||||
packed_bins_.emplace_back();
|
||||
j = placers.size() - 1;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,7 @@
|
||||
|
||||
#include "../libnest2d.hpp"
|
||||
|
||||
namespace libnest2d {
|
||||
namespace strategies {
|
||||
namespace libnest2d { namespace selections {
|
||||
|
||||
template<class RawShape>
|
||||
class SelectionBoilerplate {
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "printer_parts.h"
|
||||
#include <libnest2d/geometry_traits_nfp.hpp>
|
||||
//#include "../tools/libnfpglue.hpp"
|
||||
//#include "../tools/nfp_svgnest_glue.hpp"
|
||||
|
||||
std::vector<libnest2d::Item>& prusaParts() {
|
||||
static std::vector<libnest2d::Item> ret;
|
||||
@ -99,6 +100,43 @@ TEST(BasicFunctionality, creationAndDestruction)
|
||||
|
||||
}
|
||||
|
||||
TEST(GeometryAlgorithms, boundingCircle) {
|
||||
using namespace libnest2d;
|
||||
using placers::boundingCircle;
|
||||
|
||||
PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}};
|
||||
Circle c = boundingCircle(p);
|
||||
|
||||
ASSERT_EQ(c.center().X, 0);
|
||||
ASSERT_EQ(c.center().Y, 0);
|
||||
ASSERT_DOUBLE_EQ(c.radius(), 10);
|
||||
|
||||
shapelike::translate(p, PointImpl{10, 10});
|
||||
c = boundingCircle(p);
|
||||
|
||||
ASSERT_EQ(c.center().X, 10);
|
||||
ASSERT_EQ(c.center().Y, 10);
|
||||
ASSERT_DOUBLE_EQ(c.radius(), 10);
|
||||
|
||||
auto parts = prusaParts();
|
||||
|
||||
int i = 0;
|
||||
for(auto& part : parts) {
|
||||
c = boundingCircle(part.transformedShape());
|
||||
if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl;
|
||||
|
||||
else for(auto v : shapelike::getContour(part.transformedShape()) ) {
|
||||
auto d = pointlike::distance(v, c.center());
|
||||
if(d > c.radius() ) {
|
||||
auto e = std::abs( 1.0 - d/c.radius());
|
||||
ASSERT_LE(e, 1e-3);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST(GeometryAlgorithms, Distance) {
|
||||
using namespace libnest2d;
|
||||
|
||||
@ -107,14 +145,14 @@ TEST(GeometryAlgorithms, Distance) {
|
||||
Point p2 = {10, 0};
|
||||
Point p3 = {10, 10};
|
||||
|
||||
ASSERT_DOUBLE_EQ(PointLike::distance(p1, p2), 10);
|
||||
ASSERT_DOUBLE_EQ(PointLike::distance(p1, p3), sqrt(200));
|
||||
ASSERT_DOUBLE_EQ(pointlike::distance(p1, p2), 10);
|
||||
ASSERT_DOUBLE_EQ(pointlike::distance(p1, p3), sqrt(200));
|
||||
|
||||
Segment seg(p1, p3);
|
||||
|
||||
ASSERT_DOUBLE_EQ(PointLike::distance(p2, seg), 7.0710678118654755);
|
||||
ASSERT_DOUBLE_EQ(pointlike::distance(p2, seg), 7.0710678118654755);
|
||||
|
||||
auto result = PointLike::horizontalDistance(p2, seg);
|
||||
auto result = pointlike::horizontalDistance(p2, seg);
|
||||
|
||||
auto check = [](Coord val, Coord expected) {
|
||||
if(std::is_floating_point<Coord>::value)
|
||||
@ -127,11 +165,11 @@ TEST(GeometryAlgorithms, Distance) {
|
||||
ASSERT_TRUE(result.second);
|
||||
check(result.first, 10);
|
||||
|
||||
result = PointLike::verticalDistance(p2, seg);
|
||||
result = pointlike::verticalDistance(p2, seg);
|
||||
ASSERT_TRUE(result.second);
|
||||
check(result.first, -10);
|
||||
|
||||
result = PointLike::verticalDistance(Point{10, 20}, seg);
|
||||
result = pointlike::verticalDistance(Point{10, 20}, seg);
|
||||
ASSERT_TRUE(result.second);
|
||||
check(result.first, 10);
|
||||
|
||||
@ -139,12 +177,12 @@ TEST(GeometryAlgorithms, Distance) {
|
||||
Point p4 = {80, 0};
|
||||
Segment seg2 = { {0, 0}, {0, 40} };
|
||||
|
||||
result = PointLike::horizontalDistance(p4, seg2);
|
||||
result = pointlike::horizontalDistance(p4, seg2);
|
||||
|
||||
ASSERT_TRUE(result.second);
|
||||
check(result.first, 80);
|
||||
|
||||
result = PointLike::verticalDistance(p4, seg2);
|
||||
result = pointlike::verticalDistance(p4, seg2);
|
||||
// Point should not be related to the segment
|
||||
ASSERT_FALSE(result.second);
|
||||
|
||||
@ -172,7 +210,7 @@ TEST(GeometryAlgorithms, Area) {
|
||||
{61, 97}
|
||||
};
|
||||
|
||||
ASSERT_TRUE(ShapeLike::area(item.transformedShape()) > 0 );
|
||||
ASSERT_TRUE(shapelike::area(item.transformedShape()) > 0 );
|
||||
}
|
||||
|
||||
TEST(GeometryAlgorithms, IsPointInsidePolygon) {
|
||||
@ -182,21 +220,21 @@ TEST(GeometryAlgorithms, IsPointInsidePolygon) {
|
||||
|
||||
Point p = {1, 1};
|
||||
|
||||
ASSERT_TRUE(rect.isPointInside(p));
|
||||
ASSERT_TRUE(rect.isInside(p));
|
||||
|
||||
p = {11, 11};
|
||||
|
||||
ASSERT_FALSE(rect.isPointInside(p));
|
||||
ASSERT_FALSE(rect.isInside(p));
|
||||
|
||||
|
||||
p = {11, 12};
|
||||
|
||||
ASSERT_FALSE(rect.isPointInside(p));
|
||||
ASSERT_FALSE(rect.isInside(p));
|
||||
|
||||
|
||||
p = {3, 3};
|
||||
|
||||
ASSERT_TRUE(rect.isPointInside(p));
|
||||
ASSERT_TRUE(rect.isInside(p));
|
||||
|
||||
}
|
||||
|
||||
@ -250,7 +288,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon)
|
||||
|
||||
Item leftp(placer.leftPoly(item));
|
||||
|
||||
ASSERT_TRUE(ShapeLike::isValid(leftp.rawShape()).first);
|
||||
ASSERT_TRUE(shapelike::isValid(leftp.rawShape()).first);
|
||||
ASSERT_EQ(leftp.vertexCount(), leftControl.vertexCount());
|
||||
|
||||
for(unsigned long i = 0; i < leftControl.vertexCount(); i++) {
|
||||
@ -260,7 +298,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon)
|
||||
|
||||
Item downp(placer.downPoly(item));
|
||||
|
||||
ASSERT_TRUE(ShapeLike::isValid(downp.rawShape()).first);
|
||||
ASSERT_TRUE(shapelike::isValid(downp.rawShape()).first);
|
||||
ASSERT_EQ(downp.vertexCount(), downControl.vertexCount());
|
||||
|
||||
for(unsigned long i = 0; i < downControl.vertexCount(); i++) {
|
||||
@ -297,7 +335,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight)
|
||||
{20, 20} };
|
||||
|
||||
|
||||
Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250));
|
||||
Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250));
|
||||
|
||||
auto groups = arrange(rects.begin(), rects.end());
|
||||
|
||||
@ -350,7 +388,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose)
|
||||
|
||||
Coord min_obj_distance = 5;
|
||||
|
||||
Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250),
|
||||
Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250),
|
||||
min_obj_distance);
|
||||
|
||||
auto groups = arrange(rects.begin(), rects.end());
|
||||
@ -401,7 +439,7 @@ R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
setX(v, getX(v)/SCALE);
|
||||
rbin.setVertex(i, v);
|
||||
}
|
||||
out << ShapeLike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
|
||||
out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
|
||||
for(Item& sh : r) {
|
||||
Item tsh(sh.transformedShape());
|
||||
for(unsigned i = 0; i < tsh.vertexCount(); i++) {
|
||||
@ -410,7 +448,7 @@ R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
setX(v, getX(v)/SCALE);
|
||||
tsh.setVertex(i, v);
|
||||
}
|
||||
out << ShapeLike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
|
||||
out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
|
||||
}
|
||||
out << "\n</svg>" << std::endl;
|
||||
}
|
||||
@ -664,7 +702,7 @@ std::vector<ItemPair> nfp_concave_testdata = {
|
||||
}
|
||||
};
|
||||
|
||||
template<NfpLevel lvl, Coord SCALE>
|
||||
template<nfp::NfpLevel lvl, Coord SCALE>
|
||||
void testNfp(const std::vector<ItemPair>& testdata) {
|
||||
using namespace libnest2d;
|
||||
|
||||
@ -674,29 +712,33 @@ 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});
|
||||
|
||||
auto&& nfp = Nfp::noFitPolygon<lvl>(stationary.rawShape(),
|
||||
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);
|
||||
|
||||
int i = 0;
|
||||
auto rorbiter = orbiter.transformedShape();
|
||||
auto vo = Nfp::referenceVertex(rorbiter);
|
||||
auto vo = nfp::referenceVertex(rorbiter);
|
||||
|
||||
ASSERT_TRUE(stationary.isInside(infp));
|
||||
|
||||
@ -710,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)
|
||||
};
|
||||
@ -722,22 +764,24 @@ 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++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(GeometryAlgorithms, nfpConvexConvex) {
|
||||
testNfp<NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
|
||||
testNfp<nfp::NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
|
||||
}
|
||||
|
||||
//TEST(GeometryAlgorithms, nfpConcaveConcave) {
|
||||
@ -758,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)));
|
||||
@ -770,7 +814,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) {
|
||||
|
||||
for(int i = 0; i <= 100; i++) {
|
||||
auto v = ecache.coords(i*(0.01));
|
||||
ASSERT_TRUE(ShapeLike::touches(v, input.transformedShape()));
|
||||
ASSERT_TRUE(shapelike::touches(v, input.transformedShape()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -784,17 +828,17 @@ TEST(GeometryAlgorithms, mergePileWithPolygon) {
|
||||
rect2.translate({10, 0});
|
||||
rect3.translate({25, 0});
|
||||
|
||||
ShapeLike::Shapes<PolygonImpl> pile;
|
||||
shapelike::Shapes<PolygonImpl> pile;
|
||||
pile.push_back(rect1.transformedShape());
|
||||
pile.push_back(rect2.transformedShape());
|
||||
|
||||
auto result = Nfp::merge(pile, rect3.transformedShape());
|
||||
auto result = nfp::merge(pile, rect3.transformedShape());
|
||||
|
||||
ASSERT_EQ(result.size(), 1);
|
||||
|
||||
Rectangle ref(45, 15);
|
||||
|
||||
ASSERT_EQ(ShapeLike::area(result.front()), ref.area());
|
||||
ASSERT_EQ(shapelike::area(result.front()), ref.area());
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
@ -56,7 +56,7 @@ libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) {
|
||||
|
||||
NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
{
|
||||
using Vertex = PointImpl;
|
||||
namespace sl = shapelike;
|
||||
|
||||
NfpR ret;
|
||||
|
||||
@ -85,7 +85,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
// this can throw
|
||||
auto nfp = libnfporb::generateNFP(pstat, porb, true);
|
||||
|
||||
auto &ct = ShapeLike::getContour(ret.first);
|
||||
auto &ct = sl::getContour(ret.first);
|
||||
ct.reserve(nfp.front().size()+1);
|
||||
for(auto v : nfp.front()) {
|
||||
v = scale(v, refactor);
|
||||
@ -94,7 +94,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
ct.push_back(ct.front());
|
||||
std::reverse(ct.begin(), ct.end());
|
||||
|
||||
auto &rholes = ShapeLike::holes(ret.first);
|
||||
auto &rholes = sl::holes(ret.first);
|
||||
for(size_t hidx = 1; hidx < nfp.size(); ++hidx) {
|
||||
if(nfp[hidx].size() >= 3) {
|
||||
rholes.emplace_back();
|
||||
@ -110,31 +110,31 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
}
|
||||
}
|
||||
|
||||
ret.second = Nfp::referenceVertex(ret.first);
|
||||
ret.second = nfp::referenceVertex(ret.first);
|
||||
|
||||
} catch(std::exception& e) {
|
||||
std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl;
|
||||
// auto ch_stat = ShapeLike::convexHull(sh);
|
||||
// auto ch_orb = ShapeLike::convexHull(cother);
|
||||
ret = Nfp::nfpConvexOnly(sh, cother);
|
||||
ret = nfp::nfpConvexOnly(sh, cother);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY>::operator()(
|
||||
NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);//nfpConvexOnly(sh, cother);
|
||||
}
|
||||
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX>::operator()(
|
||||
NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
}
|
||||
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE>::operator()(
|
||||
NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
|
@ -5,22 +5,22 @@
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
using NfpR = Nfp::NfpResult<PolygonImpl>;
|
||||
using NfpR = nfp::NfpResult<PolygonImpl>;
|
||||
|
||||
NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
|
||||
struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> {
|
||||
struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
|
||||
struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
@ -34,7 +34,7 @@ struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
|
||||
// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
//};
|
||||
|
||||
template<> struct Nfp::MaxNfpLevel<PolygonImpl> {
|
||||
template<> struct nfp::MaxNfpLevel<PolygonImpl> {
|
||||
static const BP2D_CONSTEXPR NfpLevel value =
|
||||
// NfpLevel::CONVEX_ONLY;
|
||||
NfpLevel::BOTH_CONCAVE;
|
||||
|
1018
xs/src/libnest2d/tools/nfp_svgnest.hpp
Normal file
1018
xs/src/libnest2d/tools/nfp_svgnest.hpp
Normal file
File diff suppressed because it is too large
Load Diff
75
xs/src/libnest2d/tools/nfp_svgnest_glue.hpp
Normal file
75
xs/src/libnest2d/tools/nfp_svgnest_glue.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef NFP_SVGNEST_GLUE_HPP
|
||||
#define NFP_SVGNEST_GLUE_HPP
|
||||
|
||||
#include "nfp_svgnest.hpp"
|
||||
|
||||
#include <libnest2d/clipper_backend/clipper_backend.hpp>
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
namespace __svgnest {
|
||||
|
||||
//template<> struct _Tol<double> {
|
||||
// static const BP2D_CONSTEXPR TCoord<PointImpl> Value = 1000000;
|
||||
//};
|
||||
|
||||
}
|
||||
|
||||
namespace nfp {
|
||||
|
||||
using NfpR = NfpResult<PolygonImpl>;
|
||||
|
||||
template<> struct NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
|
||||
// return nfpConvexOnly(sh, cother);
|
||||
namespace sl = shapelike;
|
||||
using alg = __svgnest::_alg<PolygonImpl>;
|
||||
|
||||
auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
|
||||
sl::getContour(cother), false, false);
|
||||
|
||||
PolygonImpl nfp_cntr;
|
||||
if(!nfp_p.empty()) nfp_cntr.Contour = nfp_p.front();
|
||||
return {nfp_cntr, referenceVertex(nfp_cntr)};
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
|
||||
// return nfpConvexOnly(sh, cother);
|
||||
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();
|
||||
return {nfp_cntr, referenceVertex(nfp_cntr)};
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
|
||||
namespace sl = shapelike;
|
||||
using alg = __svgnest::_alg<PolygonImpl>;
|
||||
|
||||
auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
|
||||
sl::getContour(cother), true, false);
|
||||
|
||||
PolygonImpl nfp_cntr;
|
||||
nfp_cntr.Contour = nfp_p.front();
|
||||
return {nfp_cntr, referenceVertex(nfp_cntr)};
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct MaxNfpLevel<PolygonImpl> {
|
||||
// static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::BOTH_CONCAVE;
|
||||
static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // NFP_SVGNEST_GLUE_HPP
|
@ -56,14 +56,14 @@ public:
|
||||
auto d = static_cast<Coord>(
|
||||
std::round(conf_.height*conf_.mm_in_coord_units) );
|
||||
|
||||
auto& contour = ShapeLike::getContour(tsh);
|
||||
auto& contour = shapelike::getContour(tsh);
|
||||
for(auto& v : contour) setY(v, -getY(v) + d);
|
||||
|
||||
auto& holes = ShapeLike::holes(tsh);
|
||||
auto& holes = shapelike::holes(tsh);
|
||||
for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d);
|
||||
|
||||
}
|
||||
currentLayer() += ShapeLike::serialize<Formats::SVG>(tsh,
|
||||
currentLayer() += shapelike::serialize<Formats::SVG>(tsh,
|
||||
1.0/conf_.mm_in_coord_units) + "\n";
|
||||
}
|
||||
|
||||
|
@ -99,54 +99,55 @@ 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*/,
|
||||
ShapeLike::Shapes<PolygonImpl>& pile, // The currently arranged pile
|
||||
double /*pile_area*/,
|
||||
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 pl = PointLike;
|
||||
using sl = ShapeLike;
|
||||
using Coord = TCoord<PointImpl>;
|
||||
|
||||
static const double BIG_ITEM_TRESHOLD = 0.2;
|
||||
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 normarea = [norm](double area) { return std::sqrt(area)/norm; };
|
||||
|
||||
// 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(normarea(areacache[idx]) > BIG_ITEM_TRESHOLD)
|
||||
spatindex.insert({sl::boundingBox(p), idx});
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
auto isBig = [bin_area](double a) {
|
||||
return a/bin_area > BIG_ITEM_TRESHOLD ;
|
||||
};
|
||||
|
||||
// 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 = ShapeLike::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
|
||||
@ -157,19 +158,11 @@ objfunc(const PointImpl& bincenter,
|
||||
boost::geometry::convert(boostbb, bigbb);
|
||||
}
|
||||
|
||||
// The size indicator of the candidate item. This is not the area,
|
||||
// but almost...
|
||||
double item_normarea = normarea(item.area());
|
||||
|
||||
// Will hold the resulting score
|
||||
double score = 0;
|
||||
|
||||
if(item_normarea > BIG_ITEM_TRESHOLD) {
|
||||
if(isBig(item.area())) {
|
||||
// This branch is for the bigger items..
|
||||
// Here we will use the closest point of the item bounding box to
|
||||
// the already arranged pile. So not the bb center nor the a choosen
|
||||
// corner but whichever is the closest to the center. This will
|
||||
// prevent some unwanted strange arrangements.
|
||||
|
||||
auto minc = ibb.minCorner(); // bottom left corner
|
||||
auto maxc = ibb.maxCorner(); // top right corner
|
||||
@ -192,46 +185,62 @@ objfunc(const PointImpl& bincenter,
|
||||
auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
|
||||
|
||||
// Density is the pack density: how big is the arranged pile
|
||||
auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
double density = 0;
|
||||
|
||||
// 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
|
||||
// return the score for the best alignment. So it is enough for the
|
||||
// candidate to be aligned with only one item.
|
||||
auto alignment_score = std::numeric_limits<double>::max();
|
||||
if(remaining.empty()) {
|
||||
|
||||
auto& trsh = item.transformedShape();
|
||||
auto mp = merged_pile;
|
||||
mp.emplace_back(item.transformedShape());
|
||||
auto chull = sl::convexHull(mp);
|
||||
|
||||
auto querybb = item.boundingBox();
|
||||
placers::EdgeCache<PolygonImpl> ec(chull);
|
||||
|
||||
// Query the spatial index for the neigbours
|
||||
std::vector<SpatElement> result;
|
||||
spatindex.query(bgi::intersects(querybb), std::back_inserter(result));
|
||||
double circ = ec.circumference() / norm;
|
||||
double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm;
|
||||
score = 0.5*circ + 0.5*bcirc;
|
||||
|
||||
for(auto& e : result) { // now get the score for the best alignment
|
||||
auto idx = e.second;
|
||||
auto& p = pile[idx];
|
||||
auto parea = areacache[idx];
|
||||
auto bb = sl::boundingBox(sl::Shapes<PolygonImpl>{p, trsh});
|
||||
auto bbarea = bb.area();
|
||||
auto ascore = 1.0 - (item.area() + parea)/bbarea;
|
||||
} 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 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;
|
||||
|
||||
if(ascore < alignment_score) alignment_score = ascore;
|
||||
density = (fullbb.width()*fullbb.height()) / (norm*norm);
|
||||
auto querybb = item.boundingBox();
|
||||
|
||||
// Query the spatial index for the neighbors
|
||||
std::vector<SpatElement> result;
|
||||
result.reserve(spatindex.size());
|
||||
spatindex.query(bgi::intersects(querybb),
|
||||
std::back_inserter(result));
|
||||
|
||||
for(auto& e : result) { // now get the score for the best alignment
|
||||
auto idx = e.second;
|
||||
Item& p = items[idx];
|
||||
auto parea = p.area();
|
||||
if(std::abs(1.0 - parea/item.area()) < 1e-6) {
|
||||
auto bb = boundingBox(p.boundingBox(), ibb);
|
||||
auto bbarea = bb.area();
|
||||
auto ascore = 1.0 - (item.area() + parea)/bbarea;
|
||||
|
||||
if(ascore < alignment_score) alignment_score = ascore;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 neighbors
|
||||
if(result.empty())
|
||||
score = 0.5 * dist + 0.5 * density;
|
||||
else
|
||||
score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score;
|
||||
}
|
||||
|
||||
// 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
|
||||
auto C = 0.33;
|
||||
score = C * dist + C * density + C * alignment_score;
|
||||
|
||||
} else if( item_normarea < BIG_ITEM_TRESHOLD && spatindex.empty()) {
|
||||
// If there are no big items, only small, we should consider the
|
||||
// density here as well to not get silly results
|
||||
} else if( !isBig(item.area()) && spatindex.empty()) {
|
||||
auto bindist = pl::distance(ibb.center(), bincenter) / norm;
|
||||
auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
score = ROUNDNESS_RATIO * bindist + DENSITY_RATIO * density;
|
||||
// Bindist is surprisingly enough...
|
||||
score = bindist;
|
||||
} else {
|
||||
// Here there are the small items that should be placed around the
|
||||
// already processed bigger items.
|
||||
@ -259,7 +268,9 @@ void fillConfig(PConf& pcfg) {
|
||||
|
||||
// The accuracy of optimization.
|
||||
// Goes from 0.0 to 1.0 and scales performance as well
|
||||
pcfg.accuracy = 0.6f;
|
||||
pcfg.accuracy = 0.65f;
|
||||
|
||||
pcfg.parallel = true;
|
||||
}
|
||||
|
||||
template<class TBin>
|
||||
@ -268,31 +279,62 @@ class AutoArranger {};
|
||||
template<class TBin>
|
||||
class _ArrBase {
|
||||
protected:
|
||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>;
|
||||
|
||||
using Placer = TPacker<TBin>;
|
||||
using Selector = FirstFitSelection;
|
||||
using Packer = Arranger<Placer, Selector>;
|
||||
using Packer = Nester<Placer, Selector>;
|
||||
using PConfig = typename Packer::PlacementConfig;
|
||||
using Distance = TCoord<PointImpl>;
|
||||
using Pile = ShapeLike::Shapes<PolygonImpl>;
|
||||
using Pile = sl::Shapes<PolygonImpl>;
|
||||
|
||||
Packer pck_;
|
||||
PConfig pconf_; // Placement configuration
|
||||
double bin_area_;
|
||||
std::vector<double> areacache_;
|
||||
SpatIndex rtree_;
|
||||
double norm_;
|
||||
Pile merged_pile_;
|
||||
Box pilebb_;
|
||||
ItemGroup remaining_;
|
||||
ItemGroup items_;
|
||||
public:
|
||||
|
||||
_ArrBase(const TBin& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind):
|
||||
pck_(bin, dist), bin_area_(ShapeLike::area<PolygonImpl>(bin))
|
||||
pck_(bin, dist), bin_area_(sl::area(bin)),
|
||||
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();
|
||||
return pck_.arrangeIndexed(std::forward<Args>(args)...);
|
||||
rtree_.clear();
|
||||
return pck_.executeIndexed(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
@ -304,22 +346,69 @@ public:
|
||||
std::function<void(unsigned)> progressind):
|
||||
_ArrBase<Box>(bin, dist, progressind)
|
||||
{
|
||||
pconf_.object_function = [this, bin] (
|
||||
Pile& pile,
|
||||
const Item &item,
|
||||
double pile_area,
|
||||
double norm,
|
||||
double /*penality*/) {
|
||||
|
||||
auto result = objfunc(bin.center(), bin_area_, pile,
|
||||
pile_area, item, norm, areacache_, rtree_);
|
||||
pconf_.object_function = [this, bin] (const Item &item) {
|
||||
|
||||
auto result = objfunc(bin.center(),
|
||||
merged_pile_,
|
||||
pilebb_,
|
||||
items_,
|
||||
item,
|
||||
bin_area_,
|
||||
norm_,
|
||||
rtree_,
|
||||
remaining_);
|
||||
|
||||
double score = std::get<0>(result);
|
||||
auto& fullbb = std::get<1>(result);
|
||||
|
||||
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 miss = Placer::overfit(fullbb, bin);
|
||||
miss = miss > 0? miss : 0;
|
||||
score += miss*miss;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
pck_.configure(pconf_);
|
||||
}
|
||||
};
|
||||
|
||||
using lnCircle = libnest2d::_Circle<libnest2d::PointImpl>;
|
||||
|
||||
template<>
|
||||
class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
|
||||
public:
|
||||
|
||||
AutoArranger(const lnCircle& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind):
|
||||
_ArrBase<lnCircle>(bin, dist, progressind) {
|
||||
|
||||
pconf_.object_function = [this, &bin] (const Item &item) {
|
||||
|
||||
auto result = objfunc(bin.center(),
|
||||
merged_pile_,
|
||||
pilebb_,
|
||||
items_,
|
||||
item,
|
||||
bin_area_,
|
||||
norm_,
|
||||
rtree_,
|
||||
remaining_);
|
||||
|
||||
double score = std::get<0>(result);
|
||||
|
||||
auto isBig = [this](const Item& itm) {
|
||||
return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
@ -335,27 +424,20 @@ public:
|
||||
std::function<void(unsigned)> progressind):
|
||||
_ArrBase<PolygonImpl>(bin, dist, progressind)
|
||||
{
|
||||
pconf_.object_function = [this, &bin] (
|
||||
Pile& pile,
|
||||
const Item &item,
|
||||
double pile_area,
|
||||
double norm,
|
||||
double /*penality*/) {
|
||||
pconf_.object_function = [this, &bin] (const Item &item) {
|
||||
|
||||
auto binbb = ShapeLike::boundingBox(bin);
|
||||
auto result = objfunc(binbb.center(), bin_area_, pile,
|
||||
pile_area, item, norm, areacache_, rtree_);
|
||||
auto binbb = sl::boundingBox(bin);
|
||||
auto result = objfunc(binbb.center(),
|
||||
merged_pile_,
|
||||
pilebb_,
|
||||
items_,
|
||||
item,
|
||||
bin_area_,
|
||||
norm_,
|
||||
rtree_,
|
||||
remaining_);
|
||||
double score = std::get<0>(result);
|
||||
|
||||
pile.emplace_back(item.transformedShape());
|
||||
auto chull = ShapeLike::convexHull(pile);
|
||||
pile.pop_back();
|
||||
|
||||
// If it does not fit into the print bed we will beat it with a
|
||||
// large penality. If we would not do this, there would be only one
|
||||
// big pile that doesn't care whether it fits onto the print bed.
|
||||
if(!Placer::wouldFit(chull, bin)) score += norm;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
@ -370,15 +452,17 @@ public:
|
||||
AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
|
||||
_ArrBase<Box>(Box(0, 0), dist, progressind)
|
||||
{
|
||||
this->pconf_.object_function = [this] (
|
||||
Pile& pile,
|
||||
const Item &item,
|
||||
double pile_area,
|
||||
double norm,
|
||||
double /*penality*/) {
|
||||
this->pconf_.object_function = [this] (const Item &item) {
|
||||
|
||||
auto result = objfunc({0, 0}, 0, pile, pile_area,
|
||||
item, norm, areacache_, rtree_);
|
||||
auto result = objfunc({0, 0},
|
||||
merged_pile_,
|
||||
pilebb_,
|
||||
items_,
|
||||
item,
|
||||
0,
|
||||
norm_,
|
||||
rtree_,
|
||||
remaining_);
|
||||
return std::get<0>(result);
|
||||
};
|
||||
|
||||
@ -440,16 +524,113 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum BedShapeHint {
|
||||
class Circle {
|
||||
Point center_;
|
||||
double radius_;
|
||||
public:
|
||||
|
||||
inline Circle(): center_(0, 0), radius_(std::nan("")) {}
|
||||
inline Circle(const Point& c, double r): center_(c), radius_(r) {}
|
||||
|
||||
inline double radius() const { return radius_; }
|
||||
inline const Point& center() const { return center_; }
|
||||
inline operator bool() { return !std::isnan(radius_); }
|
||||
inline operator lnCircle() {
|
||||
return lnCircle({center_(0), center_(1)}, radius_);
|
||||
}
|
||||
};
|
||||
|
||||
enum class BedShapeType {
|
||||
BOX,
|
||||
CIRCLE,
|
||||
IRREGULAR,
|
||||
WHO_KNOWS
|
||||
};
|
||||
|
||||
BedShapeHint bedShape(const Slic3r::Polyline& /*bed*/) {
|
||||
struct BedShapeHint {
|
||||
BedShapeType type;
|
||||
/*union*/ struct { // I know but who cares...
|
||||
Circle circ;
|
||||
BoundingBox box;
|
||||
Polyline polygon;
|
||||
} shape;
|
||||
};
|
||||
|
||||
BedShapeHint bedShape(const Polyline& bed) {
|
||||
BedShapeHint ret;
|
||||
|
||||
auto x = [](const Point& p) { return p(0); };
|
||||
auto y = [](const Point& p) { return p(1); };
|
||||
|
||||
auto width = [x](const BoundingBox& box) {
|
||||
return x(box.max) - x(box.min);
|
||||
};
|
||||
|
||||
auto height = [y](const BoundingBox& box) {
|
||||
return y(box.max) - y(box.min);
|
||||
};
|
||||
|
||||
auto area = [&width, &height](const BoundingBox& box) {
|
||||
double w = width(box);
|
||||
double h = height(box);
|
||||
return w*h;
|
||||
};
|
||||
|
||||
auto poly_area = [](Polyline p) {
|
||||
Polygon pp; pp.points.reserve(p.points.size() + 1);
|
||||
pp.points = std::move(p.points);
|
||||
pp.points.emplace_back(pp.points.front());
|
||||
return std::abs(pp.area());
|
||||
};
|
||||
|
||||
auto distance_to = [x, y](const Point& p1, const Point& p2) {
|
||||
double dx = x(p2) - x(p1);
|
||||
double dy = y(p2) - y(p1);
|
||||
return std::sqrt(dx*dx + dy*dy);
|
||||
};
|
||||
|
||||
auto bb = bed.bounding_box();
|
||||
|
||||
auto isCircle = [bb, distance_to](const Polyline& polygon) {
|
||||
auto center = bb.center();
|
||||
std::vector<double> vertex_distances;
|
||||
double avg_dist = 0;
|
||||
for (auto pt: polygon.points)
|
||||
{
|
||||
double distance = distance_to(center, pt);
|
||||
vertex_distances.push_back(distance);
|
||||
avg_dist += distance;
|
||||
}
|
||||
|
||||
avg_dist /= vertex_distances.size();
|
||||
|
||||
Circle ret(center, avg_dist);
|
||||
for (auto el: vertex_distances)
|
||||
{
|
||||
if (abs(el - avg_dist) > 10 * SCALED_EPSILON)
|
||||
ret = Circle();
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
auto parea = poly_area(bed);
|
||||
|
||||
if( (1.0 - parea/area(bb)) < 1e-3 ) {
|
||||
ret.type = BedShapeType::BOX;
|
||||
ret.shape.box = bb;
|
||||
}
|
||||
else if(auto c = isCircle(bed)) {
|
||||
ret.type = BedShapeType::CIRCLE;
|
||||
ret.shape.circ = c;
|
||||
} else {
|
||||
ret.type = BedShapeType::IRREGULAR;
|
||||
ret.shape.polygon = bed;
|
||||
}
|
||||
|
||||
// Determine the bed shape by hand
|
||||
return BOX;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void applyResult(
|
||||
@ -468,8 +649,7 @@ void applyResult(
|
||||
// appropriately
|
||||
auto off = item.translation();
|
||||
Radians rot = item.rotation();
|
||||
Vec2d foff(off.X*SCALING_FACTOR + batch_offset,
|
||||
off.Y*SCALING_FACTOR);
|
||||
Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR);
|
||||
|
||||
// write the tranformation data into the model instance
|
||||
inst_ptr->rotation = rot;
|
||||
@ -525,7 +705,10 @@ bool arrange(Model &model, coordf_t min_obj_distance,
|
||||
});
|
||||
|
||||
IndexedPackGroup result;
|
||||
BoundingBox bbb(bed.points);
|
||||
|
||||
if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed);
|
||||
|
||||
BoundingBox bbb(bed);
|
||||
|
||||
auto binbb = Box({
|
||||
static_cast<libnest2d::Coord>(bbb.min(0)),
|
||||
@ -536,8 +719,8 @@ bool arrange(Model &model, coordf_t min_obj_distance,
|
||||
static_cast<libnest2d::Coord>(bbb.max(1))
|
||||
});
|
||||
|
||||
switch(bedhint) {
|
||||
case BOX: {
|
||||
switch(bedhint.type) {
|
||||
case BedShapeType::BOX: {
|
||||
|
||||
// Create the arranger for the box shaped bed
|
||||
AutoArranger<Box> arrange(binbb, min_obj_distance, progressind);
|
||||
@ -547,16 +730,22 @@ bool arrange(Model &model, coordf_t min_obj_distance,
|
||||
result = arrange(shapes.begin(), shapes.end());
|
||||
break;
|
||||
}
|
||||
case CIRCLE:
|
||||
case BedShapeType::CIRCLE: {
|
||||
|
||||
auto c = bedhint.shape.circ;
|
||||
auto cc = lnCircle(c);
|
||||
|
||||
AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind);
|
||||
result = arrange(shapes.begin(), shapes.end());
|
||||
break;
|
||||
case IRREGULAR:
|
||||
case WHO_KNOWS: {
|
||||
}
|
||||
case BedShapeType::IRREGULAR:
|
||||
case BedShapeType::WHO_KNOWS: {
|
||||
|
||||
using P = libnest2d::PolygonImpl;
|
||||
|
||||
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
||||
P irrbed = ShapeLike::create<PolygonImpl>(std::move(ctour));
|
||||
|
||||
// std::cout << ShapeLike::toString(irrbed) << std::endl;
|
||||
P irrbed = sl::create<PolygonImpl>(std::move(ctour));
|
||||
|
||||
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
|
||||
|
||||
@ -567,6 +756,8 @@ bool arrange(Model &model, coordf_t min_obj_distance,
|
||||
}
|
||||
};
|
||||
|
||||
if(result.empty()) return false;
|
||||
|
||||
if(first_bin_only) {
|
||||
applyResult(result.front(), 0, shapemap);
|
||||
} else {
|
||||
|
@ -433,6 +433,7 @@ const PrintConfig &PrintController::config() const
|
||||
return print_->config;
|
||||
}
|
||||
|
||||
|
||||
void AppController::arrange_model()
|
||||
{
|
||||
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
|
||||
@ -468,21 +469,25 @@ void AppController::arrange_model()
|
||||
if(pind) pind->update(0, _(L("Arranging objects...")));
|
||||
|
||||
try {
|
||||
arr::BedShapeHint hint;
|
||||
// TODO: from Sasha from GUI
|
||||
hint.type = arr::BedShapeType::WHO_KNOWS;
|
||||
|
||||
arr::arrange(*model_,
|
||||
min_obj_distance,
|
||||
bed,
|
||||
arr::BOX,
|
||||
false, // create many piles not just one pile
|
||||
[this, pind, count](unsigned rem) {
|
||||
min_obj_distance,
|
||||
bed,
|
||||
hint,
|
||||
false, // create many piles not just one pile
|
||||
[pind, count](unsigned rem) {
|
||||
if(pind)
|
||||
pind->update(count - rem, _(L("Arranging objects...")));
|
||||
});
|
||||
} catch(std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
report_issue(IssueType::ERR,
|
||||
_(L("Could not arrange model objects! "
|
||||
"Some geometries may be invalid.")),
|
||||
_(L("Exception occurred")));
|
||||
_(L("Could not arrange model objects! "
|
||||
"Some geometries may be invalid.")),
|
||||
_(L("Exception occurred")));
|
||||
}
|
||||
|
||||
// Restore previous max value
|
||||
|
Loading…
Reference in New Issue
Block a user