Merge remote-tracking branch 'origin/feature_arrange_with_libnest2d' into dev

# Conflicts:
#	xs/src/slic3r/AppController.cpp
This commit is contained in:
tamasmeszaros 2018-08-31 10:55:55 +02:00
commit 6e2ed48e5a
30 changed files with 3273 additions and 1687 deletions

View file

@ -813,6 +813,7 @@ add_custom_target(pot
set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d")
add_subdirectory(${LIBDIR}/libnest2d) add_subdirectory(${LIBDIR}/libnest2d)
target_compile_definitions(libslic3r PUBLIC -DUSE_TBB)
target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES}) target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES})
target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES}) target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES})

View file

@ -31,6 +31,7 @@ set(LIBNEST2D_SRCFILES
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/metaloop.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/placer_boilerplate.hpp
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp
@ -89,14 +90,39 @@ if(LIBNEST2D_UNITTESTS)
endif() endif()
if(LIBNEST2D_BUILD_EXAMPLES) if(LIBNEST2D_BUILD_EXAMPLES)
add_executable(example examples/main.cpp add_executable(example examples/main.cpp
# tools/libnfpglue.hpp # tools/libnfpglue.hpp
# tools/libnfpglue.cpp # tools/libnfpglue.cpp
tools/nfp_svgnest.hpp
tools/nfp_svgnest_glue.hpp
tools/svgtools.hpp tools/svgtools.hpp
tests/printer_parts.cpp tests/printer_parts.cpp
tests/printer_parts.h tests/printer_parts.h
${LIBNEST2D_SRCFILES}) ${LIBNEST2D_SRCFILES}
)
set(TBB_STATIC ON)
find_package(TBB QUIET)
if(TBB_FOUND)
message(STATUS "Parallelization with Intel TBB")
target_include_directories(example PUBLIC ${TBB_INCLUDE_DIRS})
target_compile_definitions(example PUBLIC ${TBB_DEFINITIONS} -DUSE_TBB)
if(MSVC)
# Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
target_compile_definitions(example PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE)
endif()
# The Intel TBB library will use the std::exception_ptr feature of C++11.
target_compile_definitions(example PUBLIC -DTBB_USE_CAPTURED_EXCEPTION=1)
target_link_libraries(example ${TBB_LIBRARIES})
else()
find_package(OpenMP QUIET)
if(OpenMP_CXX_FOUND)
message(STATUS "Parallelization with OpenMP")
target_include_directories(example PUBLIC OpenMP::OpenMP_CXX)
target_link_libraries(example OpenMP::OpenMP_CXX)
endif()
endif()
target_link_libraries(example ${LIBNEST2D_LIBRARIES}) target_link_libraries(example ${LIBNEST2D_LIBRARIES})
target_include_directories(example PUBLIC ${LIBNEST2D_HEADERS}) target_include_directories(example PUBLIC ${LIBNEST2D_HEADERS})

View file

@ -9,18 +9,28 @@ with templated geometry types. These geometries can have custom or already
existing implementation to avoid copying or having unnecessary dependencies. 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 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 fast and robust, being built on top of boost geometry and the
[polyclipping](http://www.angusj.com/delphi/clipper.php) library. Usage of [polyclipping](http://www.angusj.com/delphi/clipper.php) library. Usage of
this default backend implies the dependency on these packages as well as the this default backend implies the dependency on these packages but its header
compilation of the backend itself (The default backend is not yet header only). only as well.
This software is currently under construction and lacks a throughout This software is currently under construction and lacks a throughout
documentation and some essential algorithms as well. At this stage it works well documentation and some essential algorithms as well. At this stage it works well
for rectangles and convex closed polygons without considering holes and for rectangles and convex closed polygons without considering holes and
concavities. 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 # References
- [SVGNest](https://github.com/Jack000/SVGnest) - [SVGNest](https://github.com/Jack000/SVGnest)

View file

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

View file

@ -1,7 +1,6 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <fstream> #include <fstream>
//#define DEBUG_EXPORT_NFP //#define DEBUG_EXPORT_NFP
#include <libnest2d.h> #include <libnest2d.h>
@ -9,7 +8,11 @@
#include "tests/printer_parts.h" #include "tests/printer_parts.h"
#include "tools/benchmark.h" #include "tools/benchmark.h"
#include "tools/svgtools.hpp" #include "tools/svgtools.hpp"
#include "libnest2d/rotfinder.hpp"
//#include "tools/libnfpglue.hpp" //#include "tools/libnfpglue.hpp"
//#include "tools/nfp_svgnest_glue.hpp"
using namespace libnest2d; using namespace libnest2d;
using ItemGroup = std::vector<std::reference_wrapper<Item>>; using ItemGroup = std::vector<std::reference_wrapper<Item>>;
@ -50,499 +53,57 @@ void arrangeRectangles() {
using namespace libnest2d; using namespace libnest2d;
const int SCALE = 1000000; 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 = { std::vector<Item> rects(202, {
// {20*SCALE, 10*SCALE}, {-9945219, -3065619},
// {20*SCALE, 10*SCALE}, {-9781479, -2031780},
// {20*SCALE, 20*SCALE}, {-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; std::vector<Item> input;
input.insert(input.end(), prusaParts().begin(), prusaParts().end()); input.insert(input.end(), prusaParts().begin(), prusaParts().end());
// input.insert(input.end(), prusaExParts().begin(), prusaExParts().end()); // input.insert(input.end(), prusaExParts().begin(), prusaExParts().end());
// input.insert(input.end(), stegoParts().begin(), stegoParts().end()); // input.insert(input.end(), stegoParts().begin(), stegoParts().end());
// input.insert(input.end(), rects.begin(), rects.end()); // input.insert(input.end(), rects.begin(), rects.end());
// input.insert(input.end(), proba.begin(), proba.end());
// input.insert(input.end(), crasher.begin(), crasher.end());
Box bin(250*SCALE, 210*SCALE); Box bin(250*SCALE, 210*SCALE);
// PolygonImpl bin = { // PolygonImpl bin = {
@ -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>; auto min_obj_distance = static_cast<Coord>(6*SCALE);
using Packer = Arranger<Placer, FirstFitSelection>;
using Placer = placers::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
using Packer = Nester<Placer, FirstFitSelection>;
Packer arrange(bin, min_obj_distance); Packer arrange(bin, min_obj_distance);
@ -571,121 +134,23 @@ void arrangeRectangles() {
pconf.alignment = Placer::Config::Alignment::CENTER; pconf.alignment = Placer::Config::Alignment::CENTER;
pconf.starting_point = Placer::Config::Alignment::CENTER; pconf.starting_point = Placer::Config::Alignment::CENTER;
pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/}; pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/};
pconf.accuracy = 0.5f; pconf.accuracy = 0.65f;
pconf.parallel = true;
// 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;
// };
Packer::SelectionConfig sconf; Packer::SelectionConfig sconf;
// sconf.allow_parallel = false; // sconf.allow_parallel = false;
// sconf.force_parallel = false; // sconf.force_parallel = false;
// sconf.try_triplets = true; // sconf.try_triplets = true;
// sconf.try_reverse_order = true; // sconf.try_reverse_order = true;
// sconf.waste_increment = 0.005; // sconf.waste_increment = 0.01;
arrange.configure(pconf, sconf); arrange.configure(pconf, sconf);
arrange.progressIndicator([&](unsigned r){ arrange.progressIndicator([&](unsigned r){
// svg::SVGWriter::Config conf;
// conf.mm_in_coord_units = SCALE;
// svg::SVGWriter svgw(conf);
// svgw.setSize(bin);
// svgw.writePackGroup(arrange.lastResult());
// svgw.save("debout");
std::cout << "Remaining items: " << r << std::endl; std::cout << "Remaining items: " << r << std::endl;
})/*.useMinimumBoundigBoxRotation()*/; });
// findMinimumBoundingBoxRotations(input.begin(), input.end());
Benchmark bench; Benchmark bench;
@ -693,7 +158,7 @@ void arrangeRectangles() {
Packer::ResultType result; Packer::ResultType result;
try { try {
result = arrange.arrange(input.begin(), input.end()); result = arrange.execute(input.begin(), input.end());
} catch(GeometryException& ge) { } catch(GeometryException& ge) {
std::cerr << "Geometry error: " << ge.what() << std::endl; std::cerr << "Geometry error: " << ge.what() << std::endl;
return ; return ;
@ -707,7 +172,7 @@ void arrangeRectangles() {
std::vector<double> eff; std::vector<double> eff;
eff.reserve(result.size()); eff.reserve(result.size());
auto bin_area = ShapeLike::area<PolygonImpl>(bin); auto bin_area = sl::area(bin);
for(auto& r : result) { for(auto& r : result) {
double a = 0; double a = 0;
std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); }); 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(); } for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); }
std::cout << ") Total: " << total << std::endl; std::cout << ") Total: " << total << std::endl;
for(auto& it : input) { // for(auto& it : input) {
auto ret = ShapeLike::isValid(it.transformedShape()); // auto ret = sl::isValid(it.transformedShape());
std::cout << ret.second << std::endl; // std::cout << ret.second << std::endl;
} // }
if(total != input.size()) std::cout << "ERROR " << "could not pack " if(total != input.size()) std::cout << "ERROR " << "could not pack "
<< input.size() - total << " elements!" << input.size() - total << " elements!"
@ -744,13 +209,10 @@ void arrangeRectangles() {
SVGWriter svgw(conf); SVGWriter svgw(conf);
svgw.setSize(Box(250*SCALE, 210*SCALE)); svgw.setSize(Box(250*SCALE, 210*SCALE));
svgw.writePackGroup(result); svgw.writePackGroup(result);
// std::for_each(input.begin(), input.end(), [&svgw](Item& item){ svgw.writeItem(item);});
svgw.save("out"); svgw.save("out");
} }
int main(void /*int argc, char **argv*/) { int main(void /*int argc, char **argv*/) {
arrangeRectangles(); arrangeRectangles();
// findDegenerateCase();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View file

@ -22,6 +22,7 @@ using Point = PointImpl;
using Coord = TCoord<PointImpl>; using Coord = TCoord<PointImpl>;
using Box = _Box<PointImpl>; using Box = _Box<PointImpl>;
using Segment = _Segment<PointImpl>; using Segment = _Segment<PointImpl>;
using Circle = _Circle<PointImpl>;
using Item = _Item<PolygonImpl>; using Item = _Item<PolygonImpl>;
using Rectangle = _Rectangle<PolygonImpl>; using Rectangle = _Rectangle<PolygonImpl>;
@ -29,15 +30,12 @@ using Rectangle = _Rectangle<PolygonImpl>;
using PackGroup = _PackGroup<PolygonImpl>; using PackGroup = _PackGroup<PolygonImpl>;
using IndexedPackGroup = _IndexedPackGroup<PolygonImpl>; using IndexedPackGroup = _IndexedPackGroup<PolygonImpl>;
using FillerSelection = strategies::_FillerSelection<PolygonImpl>; using FillerSelection = selections::_FillerSelection<PolygonImpl>;
using FirstFitSelection = strategies::_FirstFitSelection<PolygonImpl>; using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>;
using DJDHeuristic = strategies::_DJDHeuristic<PolygonImpl>; using DJDHeuristic = selections::_DJDHeuristic<PolygonImpl>;
using NfpPlacer = strategies::_NofitPolyPlacer<PolygonImpl>; using NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl>;
using BottomLeftPlacer = strategies::_BottomLeftPlacer<PolygonImpl>; using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
//template<NfpLevel lvl = NfpLevel::BOTH_CONCAVE_WITH_HOLES>
//using NofitPolyPlacer = strategies::_NofitPolyPlacer<PolygonImpl, lvl>;
} }

View file

@ -36,7 +36,7 @@ using libnest2d::setX;
using libnest2d::setY; using libnest2d::setY;
using Box = libnest2d::_Box<PointImpl>; using Box = libnest2d::_Box<PointImpl>;
using Segment = libnest2d::_Segment<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> { template<> struct exterior_ring<bp2d::PolygonImpl> {
static inline bp2d::PathImpl& get(bp2d::PolygonImpl& p) { 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) { 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( static inline libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
bp2d::PolygonImpl& p) bp2d::PolygonImpl& p)
{ {
return libnest2d::ShapeLike::holes(p); return libnest2d::shapelike::holes(p);
} }
static inline const libnest2d::THolesContainer<bp2d::PolygonImpl>& get( static inline const libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
bp2d::PolygonImpl const& p) 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 libnest2d { // Now the algorithms that boost can provide...
namespace pointlike {
template<> template<>
inline double PointLike::distance(const PointImpl& p1, inline double distance(const PointImpl& p1, const PointImpl& p2 )
const PointImpl& p2 )
{ {
return boost::geometry::distance(p1, p2); return boost::geometry::distance(p1, p2);
} }
template<> template<>
inline double PointLike::distance(const PointImpl& p, inline double distance(const PointImpl& p, const bp2d::Segment& seg )
const bp2d::Segment& seg )
{ {
return boost::geometry::distance(p, seg); return boost::geometry::distance(p, seg);
} }
}
namespace shapelike {
// Tell libnest2d how to make string out of a ClipperPolygon object // Tell libnest2d how to make string out of a ClipperPolygon object
template<> template<>
inline bool ShapeLike::intersects(const PathImpl& sh1, inline bool intersects(const PathImpl& sh1, const PathImpl& sh2)
const PathImpl& sh2)
{ {
return boost::geometry::intersects(sh1, sh2); return boost::geometry::intersects(sh1, sh2);
} }
// Tell libnest2d how to make string out of a ClipperPolygon object // Tell libnest2d how to make string out of a ClipperPolygon object
template<> template<>
inline bool ShapeLike::intersects(const PolygonImpl& sh1, inline bool intersects(const PolygonImpl& sh1, const PolygonImpl& sh2)
const PolygonImpl& sh2)
{ {
return boost::geometry::intersects(sh1, sh2); return boost::geometry::intersects(sh1, sh2);
} }
// Tell libnest2d how to make string out of a ClipperPolygon object // Tell libnest2d how to make string out of a ClipperPolygon object
template<> template<>
inline bool ShapeLike::intersects(const bp2d::Segment& s1, inline bool intersects(const bp2d::Segment& s1, const bp2d::Segment& s2)
const bp2d::Segment& s2)
{ {
return boost::geometry::intersects(s1, s2); return boost::geometry::intersects(s1, s2);
} }
#ifndef DISABLE_BOOST_AREA #ifndef DISABLE_BOOST_AREA
template<> template<>
inline double ShapeLike::area(const PolygonImpl& shape) inline double area(const PolygonImpl& shape, const PolygonTag&)
{ {
return boost::geometry::area(shape); return boost::geometry::area(shape);
} }
#endif #endif
template<> template<>
inline bool ShapeLike::isInside<PolygonImpl>(const PointImpl& point, inline bool isInside(const PointImpl& point, const PolygonImpl& shape)
const PolygonImpl& shape)
{ {
return boost::geometry::within(point, shape); return boost::geometry::within(point, shape);
} }
template<> template<>
inline bool ShapeLike::isInside(const PolygonImpl& sh1, inline bool isInside(const PolygonImpl& sh1, const PolygonImpl& sh2)
const PolygonImpl& sh2)
{ {
return boost::geometry::within(sh1, sh2); return boost::geometry::within(sh1, sh2);
} }
template<> template<>
inline bool ShapeLike::touches( const PolygonImpl& sh1, inline bool touches(const PolygonImpl& sh1, const PolygonImpl& sh2)
const PolygonImpl& sh2)
{ {
return boost::geometry::touches(sh1, sh2); return boost::geometry::touches(sh1, sh2);
} }
template<> template<>
inline bool ShapeLike::touches( const PointImpl& point, inline bool touches( const PointImpl& point, const PolygonImpl& shape)
const PolygonImpl& shape)
{ {
return boost::geometry::touches(point, shape); return boost::geometry::touches(point, shape);
} }
#ifndef DISABLE_BOOST_BOUNDING_BOX #ifndef DISABLE_BOOST_BOUNDING_BOX
template<> template<>
inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh) inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&)
{ {
bp2d::Box b; bp2d::Box b;
boost::geometry::envelope(sh, b); boost::geometry::envelope(sh, b);
@ -395,7 +389,8 @@ inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh)
} }
template<> 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; bp2d::Box b;
boost::geometry::envelope(shapes, 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 #ifndef DISABLE_BOOST_CONVEX_HULL
template<> template<>
inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh) inline PolygonImpl convexHull(const PolygonImpl& sh, const PolygonTag&)
{ {
PolygonImpl ret; PolygonImpl ret;
boost::geometry::convex_hull(sh, ret); boost::geometry::convex_hull(sh, ret);
@ -413,7 +408,8 @@ inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh)
} }
template<> template<>
inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes) inline PolygonImpl convexHull(const TMultiShape<PolygonImpl>& shapes,
const MultiPolygonTag&)
{ {
PolygonImpl ret; PolygonImpl ret;
boost::geometry::convex_hull(shapes, ret); boost::geometry::convex_hull(shapes, ret);
@ -421,56 +417,17 @@ inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes)
} }
#endif #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 #ifndef DISABLE_BOOST_OFFSET
template<> template<>
inline void ShapeLike::offset(PolygonImpl& sh, bp2d::Coord distance) inline void offset(PolygonImpl& sh, bp2d::Coord distance)
{ {
PolygonImpl cpy = sh; PolygonImpl cpy = sh;
boost::geometry::buffer(cpy, sh, distance); boost::geometry::buffer(cpy, sh, distance);
} }
#endif #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 #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) const PolygonImpl& sh, double scale)
{ {
std::stringstream ss; std::stringstream ss;
@ -482,14 +439,14 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
Polygonf::ring_type ring; Polygonf::ring_type ring;
Polygonf::inner_container_type holes; 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; auto& v = *it;
ring.emplace_back(getX(v)*scale, getY(v)*scale); ring.emplace_back(getX(v)*scale, getY(v)*scale);
}; };
auto H = ShapeLike::holes(sh); auto H = shapelike::holes(sh);
for(PathImpl& h : H ) { for(PathImpl& h : H ) {
Polygonf::ring_type hf; Polygonf::ring_type hf;
for(auto it = h.begin(); it != h.end(); it++) { 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 #ifndef DISABLE_BOOST_UNSERIALIZE
template<> template<>
inline void ShapeLike::unserialize<libnest2d::Formats::SVG>( inline void unserialize<libnest2d::Formats::SVG>(
PolygonImpl& sh, PolygonImpl& sh,
const std::string& str) const std::string& str)
{ {
} }
#endif #endif
template<> inline std::pair<bool, std::string> template<> inline std::pair<bool, std::string> isValid(const PolygonImpl& sh)
ShapeLike::isValid(const PolygonImpl& sh)
{ {
std::string message; std::string message;
bool ret = boost::geometry::is_valid(sh, message); bool ret = boost::geometry::is_valid(sh, message);
return {ret, 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
}
} }

View file

@ -99,49 +99,54 @@ template<> struct PointType<PolygonImpl> {
using Type = PointImpl; using Type = PointImpl;
}; };
// Type of vertex iterator used by Clipper template<> struct PointType<PointImpl> {
template<> struct VertexIteratorType<PolygonImpl> { using Type = PointImpl;
using Type = ClipperLib::Path::iterator;
};
// Type of vertex iterator used by Clipper
template<> struct VertexConstIteratorType<PolygonImpl> {
using Type = ClipperLib::Path::const_iterator;
}; };
template<> struct CountourType<PolygonImpl> { template<> struct CountourType<PolygonImpl> {
using Type = PathImpl; using Type = PathImpl;
}; };
// Tell binpack2d how to extract the X coord from a ClipperPoint object template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; };
template<> inline TCoord<PointImpl> PointLike::x(const PointImpl& p)
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; return p.X;
} }
// Tell binpack2d how to extract the Y coord from a ClipperPoint object // Tell libnest2d how to extract the Y coord from a ClipperPoint object
template<> inline TCoord<PointImpl> PointLike::y(const PointImpl& p) template<> inline TCoord<PointImpl> y(const PointImpl& p)
{ {
return p.Y; return p.Y;
} }
// Tell binpack2d how to extract the X coord from a ClipperPoint object // Tell libnest2d how to extract the X coord from a ClipperPoint object
template<> inline TCoord<PointImpl>& PointLike::x(PointImpl& p) template<> inline TCoord<PointImpl>& x(PointImpl& p)
{ {
return p.X; return p.X;
} }
// Tell binpack2d how to extract the Y coord from a ClipperPoint object // Tell libnest2d how to extract the Y coord from a ClipperPoint object
template<> template<> inline TCoord<PointImpl>& y(PointImpl& p)
inline TCoord<PointImpl>& PointLike::y(PointImpl& p)
{ {
return p.Y; return p.Y;
} }
template<>
inline void ShapeLike::reserve(PolygonImpl& sh, size_t vertex_capacity)
{
return sh.Contour.reserve(vertex_capacity);
} }
#define DISABLE_BOOST_AREA #define DISABLE_BOOST_AREA
@ -175,16 +180,24 @@ inline double area<Orientation::COUNTER_CLOCKWISE>(const PolygonImpl& sh) {
return ClipperLib::Area(sh.Contour) + a; return ClipperLib::Area(sh.Contour) + a;
} }
} }
// Tell binpack2d how to make string out of a ClipperPolygon object namespace shapelike {
template<>
inline double ShapeLike::area(const PolygonImpl& sh) { 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); return _smartarea::area<OrientationType<PolygonImpl>::Value>(sh);
} }
template<> template<> inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance)
inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) { {
#define DISABLE_BOOST_OFFSET #define DISABLE_BOOST_OFFSET
using ClipperLib::ClipperOffset; 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 // 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; std::stringstream ss;
ss << "Contour {\n"; ss << "Contour {\n";
@ -257,37 +271,8 @@ template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
} }
template<> 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; PolygonImpl p;
p.Contour = path; p.Contour = path;
@ -308,8 +293,7 @@ template<> inline PolygonImpl ShapeLike::create(const PathImpl& path,
return p; return p;
} }
template<> inline PolygonImpl ShapeLike::create( PathImpl&& path, template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) {
HoleStore&& holes) {
PolygonImpl p; PolygonImpl p;
p.Contour.swap(path); p.Contour.swap(path);
@ -331,49 +315,49 @@ template<> inline PolygonImpl ShapeLike::create( PathImpl&& path,
return p; return p;
} }
template<> inline const THolesContainer<PolygonImpl>& template<>
ShapeLike::holes(const PolygonImpl& sh) inline const THolesContainer<PolygonImpl>& holes(const PolygonImpl& sh)
{ {
return sh.Holes; return sh.Holes;
} }
template<> inline THolesContainer<PolygonImpl>& template<> inline THolesContainer<PolygonImpl>& holes(PolygonImpl& sh)
ShapeLike::holes(PolygonImpl& sh)
{ {
return sh.Holes; return sh.Holes;
} }
template<> inline TContour<PolygonImpl>& template<>
ShapeLike::getHole(PolygonImpl& sh, unsigned long idx) inline TContour<PolygonImpl>& getHole(PolygonImpl& sh, unsigned long idx)
{ {
return sh.Holes[idx]; return sh.Holes[idx];
} }
template<> inline const TContour<PolygonImpl>& template<>
ShapeLike::getHole(const PolygonImpl& sh, unsigned long idx) inline const TContour<PolygonImpl>& getHole(const PolygonImpl& sh,
unsigned long idx)
{ {
return sh.Holes[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(); return sh.Holes.size();
} }
template<> inline PathImpl& ShapeLike::getContour(PolygonImpl& sh) template<> inline PathImpl& getContour(PolygonImpl& sh)
{ {
return sh.Contour; return sh.Contour;
} }
template<> template<>
inline const PathImpl& ShapeLike::getContour(const PolygonImpl& sh) inline const PathImpl& getContour(const PolygonImpl& sh)
{ {
return sh.Contour; return sh.Contour;
} }
#define DISABLE_BOOST_TRANSLATE #define DISABLE_BOOST_TRANSLATE
template<> 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& p : sh.Contour) { p += offs; }
for(auto& hole : sh.Holes) for(auto& p : hole) { 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 #define DISABLE_BOOST_ROTATE
template<> template<>
inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads) inline void rotate(PolygonImpl& sh, const Radians& rads)
{ {
using Coord = TCoord<PointImpl>; using Coord = TCoord<PointImpl>;
@ -402,9 +386,11 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
} }
} }
} // namespace shapelike
#define DISABLE_BOOST_NFP_MERGE #define DISABLE_BOOST_NFP_MERGE
inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) { inline std::vector<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
Nfp::Shapes<PolygonImpl> retv; shapelike::Shapes<PolygonImpl> retv;
ClipperLib::PolyTree result; ClipperLib::PolyTree result;
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative); clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
@ -438,8 +424,10 @@ inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
return retv; return retv;
} }
template<> inline Nfp::Shapes<PolygonImpl> namespace nfp {
Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
template<> inline std::vector<PolygonImpl>
merge(const std::vector<PolygonImpl>& shapes)
{ {
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution); ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
@ -461,6 +449,8 @@ Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
} }
}
//#define DISABLE_BOOST_SERIALIZE //#define DISABLE_BOOST_SERIALIZE
//#define DISABLE_BOOST_UNSERIALIZE //#define DISABLE_BOOST_UNSERIALIZE

View file

@ -22,34 +22,12 @@ template<class GeomType>
using TCoord = typename CoordType<remove_cvref_t<GeomType>>::Type; using TCoord = typename CoordType<remove_cvref_t<GeomType>>::Type;
/// Getting the type of point structure used by a shape. /// 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`. /// TPoint<ShapeClass> as shorthand for `typename PointType<ShapeClass>::Type`.
template<class Shape> template<class Shape>
using TPoint = typename PointType<remove_cvref_t<Shape>>::Type; 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, ...). * \brief A point pair base class for other point pairs (segment, box, ...).
* \tparam RawPoint The actual point type to use. * \tparam RawPoint The actual point type to use.
@ -60,6 +38,17 @@ struct PointPair {
RawPoint p2; 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; * \brief An abstraction of a box;
*/ */
@ -69,6 +58,9 @@ class _Box: PointPair<RawPoint> {
using PointPair<RawPoint>::p2; using PointPair<RawPoint>::p2;
public: public:
using Tag = BoxTag;
using PointType = RawPoint;
inline _Box() = default; inline _Box() = default;
inline _Box(const RawPoint& p, const RawPoint& pp): inline _Box(const RawPoint& p, const RawPoint& pp):
PointPair<RawPoint>({p, pp}) {} PointPair<RawPoint>({p, pp}) {}
@ -98,6 +90,9 @@ class _Circle {
double radius_ = 0; double radius_ = 0;
public: public:
using Tag = CircleTag;
using PointType = RawPoint;
_Circle() = default; _Circle() = default;
_Circle(const RawPoint& center, double r): center_(center), radius_(r) {} _Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
@ -109,7 +104,7 @@ public:
inline void radius(double r) { radius_ = r; } inline void radius(double r) { radius_ = r; }
inline double area() const BP2D_NOEXCEPT { 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(""); mutable Radians angletox_ = std::nan("");
public: public:
using PointType = RawPoint;
inline _Segment() = default; inline _Segment() = default;
inline _Segment(const RawPoint& p, const RawPoint& pp): inline _Segment(const RawPoint& p, const RawPoint& pp):
@ -156,36 +153,36 @@ public:
inline double length(); 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. // used in friend declarations.
struct PointLike { namespace pointlike {
template<class RawPoint> template<class RawPoint>
static TCoord<RawPoint> x(const RawPoint& p) inline TCoord<RawPoint> x(const RawPoint& p)
{ {
return p(0); return p(0);
} }
template<class RawPoint> template<class RawPoint>
static TCoord<RawPoint> y(const RawPoint& p) inline TCoord<RawPoint> y(const RawPoint& p)
{ {
return p(1); return p(1);
} }
template<class RawPoint> template<class RawPoint>
static TCoord<RawPoint>& x(RawPoint& p) inline TCoord<RawPoint>& x(RawPoint& p)
{ {
return p(0); return p(0);
} }
template<class RawPoint> template<class RawPoint>
static TCoord<RawPoint>& y(RawPoint& p) inline TCoord<RawPoint>& y(RawPoint& p)
{ {
return p(1); return p(1);
} }
template<class RawPoint> 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, static_assert(always_false<RawPoint>::value,
"PointLike::distance(point, point) unimplemented!"); "PointLike::distance(point, point) unimplemented!");
@ -193,7 +190,7 @@ struct PointLike {
} }
template<class RawPoint> template<class RawPoint>
static double distance(const RawPoint& /*p1*/, inline double distance(const RawPoint& /*p1*/,
const _Segment<RawPoint>& /*s*/) const _Segment<RawPoint>& /*s*/)
{ {
static_assert(always_false<RawPoint>::value, static_assert(always_false<RawPoint>::value,
@ -202,13 +199,13 @@ struct PointLike {
} }
template<class RawPoint> template<class RawPoint>
static std::pair<TCoord<RawPoint>, bool> horizontalDistance( inline std::pair<TCoord<RawPoint>, bool> horizontalDistance(
const RawPoint& p, const _Segment<RawPoint>& s) const RawPoint& p, const _Segment<RawPoint>& s)
{ {
using Unit = TCoord<RawPoint>; using Unit = TCoord<RawPoint>;
auto x = PointLike::x(p), y = PointLike::y(p); auto x = pointlike::x(p), y = pointlike::y(p);
auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first()); auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first());
auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second()); auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second());
TCoord<RawPoint> ret; TCoord<RawPoint> ret;
@ -228,13 +225,13 @@ struct PointLike {
} }
template<class RawPoint> template<class RawPoint>
static std::pair<TCoord<RawPoint>, bool> verticalDistance( inline std::pair<TCoord<RawPoint>, bool> verticalDistance(
const RawPoint& p, const _Segment<RawPoint>& s) const RawPoint& p, const _Segment<RawPoint>& s)
{ {
using Unit = TCoord<RawPoint>; using Unit = TCoord<RawPoint>;
auto x = PointLike::x(p), y = PointLike::y(p); auto x = pointlike::x(p), y = pointlike::y(p);
auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first()); auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first());
auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second()); auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second());
TCoord<RawPoint> ret; TCoord<RawPoint> ret;
@ -252,36 +249,36 @@ struct PointLike {
return {ret, true}; return {ret, true};
} }
}; }
template<class RawPoint> template<class RawPoint>
TCoord<RawPoint> _Box<RawPoint>::width() const BP2D_NOEXCEPT 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> template<class RawPoint>
TCoord<RawPoint> _Box<RawPoint>::height() const BP2D_NOEXCEPT 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> 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> 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> template<class RawPoint>
void setX(RawPoint& p, const TCoord<RawPoint>& val) void setX(RawPoint& p, const TCoord<RawPoint>& val)
{ {
PointLike::x<RawPoint>(p) = val; pointlike::x<RawPoint>(p) = val;
} }
template<class RawPoint> template<class RawPoint>
void setY(RawPoint& p, const TCoord<RawPoint>& val) void setY(RawPoint& p, const TCoord<RawPoint>& val)
{ {
PointLike::y<RawPoint>(p) = val; pointlike::y<RawPoint>(p) = val;
} }
template<class RawPoint> template<class RawPoint>
@ -303,7 +300,7 @@ inline Radians _Segment<RawPoint>::angleToXaxis() const
template<class RawPoint> template<class RawPoint>
inline double _Segment<RawPoint>::length() inline double _Segment<RawPoint>::length()
{ {
return PointLike::distance(first(), second()); return pointlike::distance(first(), second());
} }
template<class RawPoint> template<class RawPoint>
@ -313,9 +310,9 @@ inline RawPoint _Box<RawPoint>::center() const BP2D_NOEXCEPT {
using Coord = TCoord<RawPoint>; using Coord = TCoord<RawPoint>;
RawPoint ret = { RawPoint ret = { // No rounding here, we dont know if these are int coords
static_cast<Coord>( std::round((getX(minc) + getX(maxc))/2.0) ), static_cast<Coord>( (getX(minc) + getX(maxc))/2.0 ),
static_cast<Coord>( std::round((getY(minc) + getY(maxc))/2.0) ) static_cast<Coord>( (getY(minc) + getY(maxc))/2.0 )
}; };
return ret; return ret;
@ -356,124 +353,125 @@ enum class Formats {
// This struct serves as a namespace. The only difference is that it can be // 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. // used in friend declarations and can be aliased at class scope.
struct ShapeLike { namespace shapelike {
template<class RawShape> template<class RawShape>
using Shapes = std::vector<RawShape>; using Shapes = TMultiShape<RawShape>;
template<class RawShape> template<class RawShape>
static RawShape create(const TContour<RawShape>& contour, inline RawShape create(const TContour<RawShape>& contour,
const THolesContainer<RawShape>& holes) const THolesContainer<RawShape>& holes)
{ {
return RawShape(contour, holes); return RawShape(contour, holes);
} }
template<class RawShape> template<class RawShape>
static RawShape create(TContour<RawShape>&& contour, inline RawShape create(TContour<RawShape>&& contour,
THolesContainer<RawShape>&& holes) THolesContainer<RawShape>&& holes)
{ {
return RawShape(contour, holes); return RawShape(contour, holes);
} }
template<class RawShape> template<class RawShape>
static RawShape create(const TContour<RawShape>& contour) inline RawShape create(const TContour<RawShape>& contour)
{ {
return create<RawShape>(contour, {}); return create<RawShape>(contour, {});
} }
template<class RawShape> template<class RawShape>
static RawShape create(TContour<RawShape>&& contour) inline RawShape create(TContour<RawShape>&& contour)
{ {
return create<RawShape>(contour, {}); return create<RawShape>(contour, {});
} }
template<class RawShape> template<class RawShape>
static THolesContainer<RawShape>& holes(RawShape& /*sh*/) inline THolesContainer<RawShape>& holes(RawShape& /*sh*/)
{ {
static THolesContainer<RawShape> empty; static THolesContainer<RawShape> empty;
return empty; return empty;
} }
template<class RawShape> template<class RawShape>
static const THolesContainer<RawShape>& holes(const RawShape& /*sh*/) inline const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
{ {
static THolesContainer<RawShape> empty; static THolesContainer<RawShape> empty;
return empty; return empty;
} }
template<class RawShape> 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]; return holes(sh)[idx];
} }
template<class RawShape> template<class RawShape>
static const TContour<RawShape>& getHole(const RawShape& sh, inline const TContour<RawShape>& getHole(const RawShape& sh,
unsigned long idx) unsigned long idx)
{ {
return holes(sh)[idx]; return holes(sh)[idx];
} }
template<class RawShape> template<class RawShape>
static size_t holeCount(const RawShape& sh) inline size_t holeCount(const RawShape& sh)
{ {
return holes(sh).size(); return holes(sh).size();
} }
template<class RawShape> template<class RawShape>
static TContour<RawShape>& getContour(RawShape& sh) inline TContour<RawShape>& getContour(RawShape& sh)
{ {
return sh; return sh;
} }
template<class RawShape> template<class RawShape>
static const TContour<RawShape>& getContour(const RawShape& sh) inline const TContour<RawShape>& getContour(const RawShape& sh)
{ {
return sh; return sh;
} }
// Optional, does nothing by default // Optional, does nothing by default
template<class RawShape> 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> 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)...); return getContour(sh).emplace_back(std::forward<Args>(args)...);
} }
template<class RawShape> 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> 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> 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> 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> template<class RawShape>
static std::string toString(const RawShape& /*sh*/) inline std::string toString(const RawShape& /*sh*/)
{ {
return ""; return "";
} }
template<Formats, class RawShape> 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, static_assert(always_false<RawShape>::value,
"ShapeLike::serialize() unimplemented!"); "ShapeLike::serialize() unimplemented!");
@ -481,14 +479,14 @@ struct ShapeLike {
} }
template<Formats, class RawShape> 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, static_assert(always_false<RawShape>::value,
"ShapeLike::unserialize() unimplemented!"); "ShapeLike::unserialize() unimplemented!");
} }
template<class RawShape> template<class RawShape>
static double area(const RawShape& /*sh*/) inline double area(const RawShape& /*sh*/, const PolygonTag&)
{ {
static_assert(always_false<RawShape>::value, static_assert(always_false<RawShape>::value,
"ShapeLike::area() unimplemented!"); "ShapeLike::area() unimplemented!");
@ -496,7 +494,7 @@ struct ShapeLike {
} }
template<class RawShape> 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, static_assert(always_false<RawShape>::value,
"ShapeLike::intersects() unimplemented!"); "ShapeLike::intersects() unimplemented!");
@ -504,7 +502,7 @@ struct ShapeLike {
} }
template<class RawShape> template<class RawShape>
static bool isInside(const TPoint<RawShape>& /*point*/, inline bool isInside(const TPoint<RawShape>& /*point*/,
const RawShape& /*shape*/) const RawShape& /*shape*/)
{ {
static_assert(always_false<RawShape>::value, static_assert(always_false<RawShape>::value,
@ -513,7 +511,7 @@ struct ShapeLike {
} }
template<class RawShape> template<class RawShape>
static bool isInside(const RawShape& /*shape*/, inline bool isInside(const RawShape& /*shape*/,
const RawShape& /*shape*/) const RawShape& /*shape*/)
{ {
static_assert(always_false<RawShape>::value, static_assert(always_false<RawShape>::value,
@ -522,7 +520,7 @@ struct ShapeLike {
} }
template<class RawShape> template<class RawShape>
static bool touches( const RawShape& /*shape*/, inline bool touches( const RawShape& /*shape*/,
const RawShape& /*shape*/) const RawShape& /*shape*/)
{ {
static_assert(always_false<RawShape>::value, static_assert(always_false<RawShape>::value,
@ -531,7 +529,7 @@ struct ShapeLike {
} }
template<class RawShape> template<class RawShape>
static bool touches( const TPoint<RawShape>& /*point*/, inline bool touches( const TPoint<RawShape>& /*point*/,
const RawShape& /*shape*/) const RawShape& /*shape*/)
{ {
static_assert(always_false<RawShape>::value, static_assert(always_false<RawShape>::value,
@ -540,64 +538,66 @@ struct ShapeLike {
} }
template<class RawShape> 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, static_assert(always_false<RawShape>::value,
"ShapeLike::boundingBox(shape) unimplemented!"); "ShapeLike::boundingBox(shape) unimplemented!");
} }
template<class RawShape> template<class RawShapes>
static _Box<TPoint<RawShape>> boundingBox(const Shapes<RawShape>& /*sh*/) 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!"); "ShapeLike::boundingBox(shapes) unimplemented!");
} }
template<class RawShape> template<class RawShape>
static RawShape convexHull(const RawShape& /*sh*/) inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&)
{ {
static_assert(always_false<RawShape>::value, static_assert(always_false<RawShape>::value,
"ShapeLike::convexHull(shape) unimplemented!"); "ShapeLike::convexHull(shape) unimplemented!");
return RawShape(); return RawShape();
} }
template<class RawShape> template<class RawShapes>
static RawShape convexHull(const Shapes<RawShape>& /*sh*/) 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!"); "ShapeLike::convexHull(shapes) unimplemented!");
return RawShape(); return typename RawShapes::value_type();
} }
template<class RawShape> 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, static_assert(always_false<RawShape>::value,
"ShapeLike::rotate() unimplemented!"); "ShapeLike::rotate() unimplemented!");
} }
template<class RawShape, class RawPoint> 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, static_assert(always_false<RawShape>::value,
"ShapeLike::translate() unimplemented!"); "ShapeLike::translate() unimplemented!");
} }
template<class RawShape> 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, dout() << "The current geometry backend does not support offsetting!\n";
"ShapeLike::offset() unimplemented!");
} }
template<class RawShape> 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!"}; return {false, "ShapeLike::isValid() unimplemented!"};
} }
template<class RawShape> template<class RawShape>
static inline bool isConvex(const TContour<RawShape>& sh) inline bool isConvex(const TContour<RawShape>& sh)
{ {
using Vertex = TPoint<RawShape>; using Vertex = TPoint<RawShape>;
auto first = sh.begin(); auto first = sh.begin();
@ -633,43 +633,55 @@ struct ShapeLike {
// No need to implement these // No need to implement these
// ************************************************************************* // *************************************************************************
template<class RawShape> template<class Box>
static inline _Box<TPoint<RawShape>> boundingBox( inline Box boundingBox(const Box& box, const BoxTag& )
const _Box<TPoint<RawShape>>& box)
{ {
return box; return box;
} }
template<class RawShape> template<class Circle>
static inline _Box<TPoint<RawShape>> boundingBox( inline _Box<typename Circle::PointType> boundingBox(
const _Circle<TPoint<RawShape>>& circ) const Circle& circ, const CircleTag&)
{ {
using Coord = TCoord<TPoint<RawShape>>; using Point = typename Circle::PointType;
TPoint<RawShape> pmin = { using Coord = TCoord<Point>;
Point pmin = {
static_cast<Coord>(getX(circ.center()) - circ.radius()), static_cast<Coord>(getX(circ.center()) - circ.radius()),
static_cast<Coord>(getY(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>(getX(circ.center()) + circ.radius()),
static_cast<Coord>(getY(circ.center()) + circ.radius()) }; static_cast<Coord>(getY(circ.center()) + circ.radius()) };
return {pmin, pmax}; return {pmin, pmax};
} }
template<class RawShape> template<class S> // Dispatch function
static inline double area(const _Box<TPoint<RawShape>>& box) inline _Box<TPoint<S>> boundingBox(const S& sh)
{ {
return static_cast<double>(box.width() * box.height()); return boundingBox(sh, Tag<S>() );
} }
template<class RawShape> template<class Box>
static inline double area(const _Circle<TPoint<RawShape>>& circ) 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(); return circ.area();
} }
template<class RawShape> // Dispatching function
inline double area(const RawShape& sh)
{
return area(sh, Tag<RawShape>());
}
template<class 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, return std::accumulate(shapes.begin(), shapes.end(), 0.0,
[](double a, const RawShape& b) { [](double a, const RawShape& b) {
@ -678,14 +690,21 @@ struct ShapeLike {
} }
template<class RawShape> template<class RawShape>
static bool isInside(const TPoint<RawShape>& point, inline auto convexHull(const RawShape& sh)
const _Circle<TPoint<RawShape>>& circ) -> 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> 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) const _Box<TPoint<RawShape>>& box)
{ {
auto px = getX(point); auto px = getX(point);
@ -699,7 +718,7 @@ struct ShapeLike {
} }
template<class RawShape> template<class RawShape>
static bool isInside(const RawShape& sh, inline bool isInside(const RawShape& sh,
const _Circle<TPoint<RawShape>>& circ) const _Circle<TPoint<RawShape>>& circ)
{ {
return std::all_of(cbegin(sh), cend(sh), return std::all_of(cbegin(sh), cend(sh),
@ -709,7 +728,7 @@ struct ShapeLike {
} }
template<class RawShape> template<class RawShape>
static bool isInside(const _Box<TPoint<RawShape>>& box, inline bool isInside(const _Box<TPoint<RawShape>>& box,
const _Circle<TPoint<RawShape>>& circ) const _Circle<TPoint<RawShape>>& circ)
{ {
return isInside<RawShape>(box.minCorner(), circ) && return isInside<RawShape>(box.minCorner(), circ) &&
@ -717,7 +736,7 @@ struct ShapeLike {
} }
template<class RawShape> template<class RawShape>
static bool isInside(const _Box<TPoint<RawShape>>& ibb, inline bool isInside(const _Box<TPoint<RawShape>>& ibb,
const _Box<TPoint<RawShape>>& box) const _Box<TPoint<RawShape>>& box)
{ {
auto iminX = getX(ibb.minCorner()); auto iminX = getX(ibb.minCorner());
@ -734,31 +753,31 @@ struct ShapeLike {
} }
template<class RawShape> // Potential O(1) implementation may exist 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); return *(begin(sh) + idx);
} }
template<class RawShape> // Potential O(1) implementation may exist 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) unsigned long idx)
{ {
return *(cbegin(sh) + idx); return *(cbegin(sh) + idx);
} }
template<class RawShape> template<class RawShape>
static inline size_t contourVertexCount(const RawShape& sh) inline size_t contourVertexCount(const RawShape& sh)
{ {
return cend(sh) - cbegin(sh); return cend(sh) - cbegin(sh);
} }
template<class RawShape, class Fn> 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); for(auto it = begin(sh); it != end(sh); ++it) fn(*it);
} }
template<class RawShape, class Fn> 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) { for(int i = 0; i < holeCount(sh); ++i) {
auto& h = getHole(sh, i); auto& h = getHole(sh, i);
for(auto it = begin(h); it != end(h); ++it) fn(*it); for(auto it = begin(h); it != end(h); ++it) fn(*it);
@ -766,12 +785,12 @@ struct ShapeLike {
} }
template<class RawShape, class Fn> 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); for(auto it = cbegin(sh); it != cend(sh); ++it) fn(*it);
} }
template<class RawShape, class Fn> 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) { for(int i = 0; i < holeCount(sh); ++i) {
auto& h = getHole(sh, i); auto& h = getHole(sh, i);
for(auto it = cbegin(h); it != cend(h); ++it) fn(*it); for(auto it = cbegin(h); it != cend(h); ++it) fn(*it);
@ -779,18 +798,27 @@ struct ShapeLike {
} }
template<class RawShape, class Fn> template<class RawShape, class Fn>
static inline void foreachVertex(RawShape& sh, Fn fn) { inline void foreachVertex(RawShape& sh, Fn fn) {
foreachContourVertex(sh, fn); foreachContourVertex(sh, fn);
foreachHoleVertex(sh, fn); foreachHoleVertex(sh, fn);
} }
template<class RawShape, class 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); foreachContourVertex(sh, fn);
foreachHoleVertex(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>
} }

View file

@ -9,6 +9,27 @@
namespace libnest2d { 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. /// The complexity level of a polygon that an NFP implementation can handle.
enum class NfpLevel: unsigned { enum class NfpLevel: unsigned {
CONVEX_ONLY, CONVEX_ONLY,
@ -18,12 +39,17 @@ enum class NfpLevel: unsigned {
BOTH_CONCAVE_WITH_HOLES BOTH_CONCAVE_WITH_HOLES
}; };
/// A collection of static methods for handling the no fit polygon creation. template<class RawShape>
struct Nfp { 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 // Shorthand for a pile of polygons
template<class RawShape> template<class RawShape>
using Shapes = typename ShapeLike::Shapes<RawShape>; using Shapes = TMultiShape<RawShape>;
/** /**
* Merge a bunch of polygons with the specified additional polygon. * 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 * 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. * polygons are disjuct than the resulting set will contain more polygons.
*/ */
template<class RawShape> template<class RawShapes>
static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/) inline RawShapes merge(const RawShapes& /*shc*/)
{ {
static_assert(always_false<RawShape>::value, static_assert(always_false<RawShapes>::value,
"Nfp::merge(shapes, shape) unimplemented!"); "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. * polygons are disjuct than the resulting set will contain more polygons.
*/ */
template<class RawShape> template<class RawShape>
static Shapes<RawShape> merge(const Shapes<RawShape>& shc, inline TMultiShape<RawShape> merge(const TMultiShape<RawShape>& shc,
const RawShape& sh) const RawShape& sh)
{ {
auto m = merge(shc); auto m = nfp::merge(shc);
m.push_back(sh); 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. * reference will be always the same for the same polygon.
*/ */
template<class RawShape> template<class RawShape>
inline static TPoint<RawShape> referenceVertex(const RawShape& sh) inline TPoint<RawShape> referenceVertex(const RawShape& sh)
{ {
return rightmostUpVertex(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. * The "trivial" Cuninghame-Green implementation of NFP for convex polygons.
* *
@ -139,11 +153,11 @@ static NfpResult<RawShape> noFitPolygon(const RawShape& sh,
* *
*/ */
template<class RawShape> template<class RawShape>
static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh, inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
const RawShape& other) const RawShape& other)
{ {
using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>; using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>;
using sl = ShapeLike; namespace sl = shapelike;
RawShape rsh; // Final nfp placeholder RawShape rsh; // Final nfp placeholder
Vertex top_nfp; Vertex top_nfp;
@ -187,7 +201,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
sl::addVertex(rsh, edgelist.front().second()); sl::addVertex(rsh, edgelist.front().second());
// Sorting function for the nfp reference vertex search // Sorting function for the nfp reference vertex search
auto& cmp = _vsort<RawShape>; auto& cmp = __nfp::_vsort<RawShape>;
// the reference (rightmost top) vertex so far // the reference (rightmost top) vertex so far
top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp ); 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> template<class RawShape>
static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary, NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
const RawShape& cother) const RawShape& cother)
{ {
@ -233,7 +247,7 @@ static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
using Vertex = TPoint<RawShape>; using Vertex = TPoint<RawShape>;
using Coord = TCoord<Vertex>; using Coord = TCoord<Vertex>;
using Edge = _Segment<Vertex>; using Edge = _Segment<Vertex>;
using sl = ShapeLike; namespace sl = shapelike;
using std::signbit; using std::signbit;
using std::sort; using std::sort;
using std::vector; using std::vector;
@ -528,27 +542,16 @@ struct NfpImpl {
} }
}; };
template<class RawShape> struct MaxNfpLevel { /// Helper function to get the NFP
static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY; template<NfpLevel nfptype, class RawShape>
}; inline NfpResult<RawShape> noFitPolygon(const RawShape& sh,
const RawShape& other)
private:
// Do not specialize this...
template<class RawShape>
static inline bool _vsort(const TPoint<RawShape>& v1,
const TPoint<RawShape>& v2)
{ {
using Coord = TCoord<TPoint<RawShape>>; NfpImpl<RawShape, nfptype> nfps;
Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2); return nfps(sh, other);
auto diff = y1 - y2;
if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon())
return x1 < x2;
return diff < 0;
} }
}; }
} }

View file

@ -9,10 +9,12 @@
#include <functional> #include <functional>
#include "geometry_traits.hpp" #include "geometry_traits.hpp"
#include "optimizer.hpp"
namespace libnest2d { namespace libnest2d {
namespace sl = shapelike;
namespace pl = pointlike;
/** /**
* \brief An item to be placed on a bin. * \brief An item to be placed on a bin.
* *
@ -28,7 +30,8 @@ class _Item {
using Coord = TCoord<TPoint<RawShape>>; using Coord = TCoord<TPoint<RawShape>>;
using Vertex = TPoint<RawShape>; using Vertex = TPoint<RawShape>;
using Box = _Box<Vertex>; using Box = _Box<Vertex>;
using sl = ShapeLike;
using VertexConstIterator = typename TContour<RawShape>::const_iterator;
// The original shape that gets encapsulated. // The original shape that gets encapsulated.
RawShape sh_; RawShape sh_;
@ -38,7 +41,7 @@ class _Item {
Radians rotation_; Radians rotation_;
Coord offset_distance_; 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 // 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. // that a zero angle is not a rotation because of testing for equality.
bool has_rotation_ = false, has_translation_ = false, has_offset_ = false; bool has_rotation_ = false, has_translation_ = false, has_offset_ = false;
@ -58,12 +61,12 @@ class _Item {
}; };
mutable Convexity convexity_ = Convexity::UNCHECKED; mutable Convexity convexity_ = Convexity::UNCHECKED;
mutable TVertexConstIterator<RawShape> rmt_; // rightmost top vertex mutable VertexConstIterator rmt_; // rightmost top vertex
mutable TVertexConstIterator<RawShape> lmb_; // leftmost bottom vertex mutable VertexConstIterator lmb_; // leftmost bottom vertex
mutable bool rmt_valid_ = false, lmb_valid_ = false; mutable bool rmt_valid_ = false, lmb_valid_ = false;
mutable struct BBCache { mutable struct BBCache {
Box bb; bool valid; Vertex tr; Box bb; bool valid;
BBCache(): valid(false), tr(0, 0) {} BBCache(): valid(false) {}
} bb_cache_; } bb_cache_;
public: public:
@ -80,7 +83,7 @@ public:
* supports. Giving out a non const iterator would make it impossible to * supports. Giving out a non const iterator would make it impossible to
* perform correct cache invalidation. * perform correct cache invalidation.
*/ */
using Iterator = TVertexConstIterator<RawShape>; using Iterator = VertexConstIterator;
/** /**
* @brief Get the orientation of the polygon. * @brief Get the orientation of the polygon.
@ -109,7 +112,7 @@ public:
explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {} 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. * @param il The initializer list of vertices.
*/ */
inline _Item(const std::initializer_list< Vertex >& il): 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 * Note that the vertex considered here is taken from the original shape
* that this item is constructed from. This means that no transformation is * that this item is constructed from. This means that no transformation is
@ -244,7 +247,7 @@ public:
* @param p * @param p
* @return * @return
*/ */
inline bool isPointInside(const Vertex& p) const inline bool isInside(const Vertex& p) const
{ {
return sl::isInside(p, transformedShape()); return sl::isInside(p, transformedShape());
} }
@ -307,7 +310,7 @@ public:
{ {
if(translation_ != tr) { if(translation_ != tr) {
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false; translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
bb_cache_.valid = false; //bb_cache_.valid = false;
} }
} }
@ -342,13 +345,19 @@ public:
inline Box boundingBox() const { inline Box boundingBox() const {
if(!bb_cache_.valid) { if(!bb_cache_.valid) {
bb_cache_.bb = sl::boundingBox(transformedShape()); if(!has_rotation_)
bb_cache_.tr = {0, 0}; bb_cache_.bb = sl::boundingBox(offsettedShape());
else {
// TODO make sure this works
auto rotsh = offsettedShape();
sl::rotate(rotsh, rotation_);
bb_cache_.bb = sl::boundingBox(rotsh);
}
bb_cache_.valid = true; bb_cache_.valid = true;
} }
auto &bb = bb_cache_.bb; auto &tr = bb_cache_.tr; auto &bb = bb_cache_.bb; auto &tr = translation_;
return {bb.minCorner() + tr, bb.maxCorner() + tr}; return {bb.minCorner() + tr, bb.maxCorner() + tr };
} }
inline Vertex referenceVertex() const { inline Vertex referenceVertex() const {
@ -438,7 +447,7 @@ public:
inline _Rectangle(Unit width, Unit height, inline _Rectangle(Unit width, Unit height,
// disable this ctor if o != CLOCKWISE // disable this ctor if o != CLOCKWISE
enable_if_t< o == TO::CLOCKWISE, int> = 0 ): enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
_Item<RawShape>( ShapeLike::create<RawShape>( { _Item<RawShape>( sl::create<RawShape>( {
{0, 0}, {0, 0},
{0, height}, {0, height},
{width, height}, {width, height},
@ -452,7 +461,7 @@ public:
inline _Rectangle(Unit width, Unit height, inline _Rectangle(Unit width, Unit height,
// disable this ctor if o != COUNTER_CLOCKWISE // disable this ctor if o != COUNTER_CLOCKWISE
enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ): enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
_Item<RawShape>( ShapeLike::create<RawShape>( { _Item<RawShape>( sl::create<RawShape>( {
{0, 0}, {0, 0},
{width, 0}, {width, 0},
{width, height}, {width, height},
@ -473,18 +482,38 @@ public:
template<class RawShape> template<class RawShape>
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const { 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 template<class RawShape> inline bool
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const { _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. * \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 * specialize this class template and define all the ten methods it has. It can
* use the strategies::PlacerBoilerplace class for creating a new placement * use the strategies::PlacerBoilerplace class for creating a new placement
* strategy where only the constructor and the trypack method has to be provided * strategy where only the constructor and the trypack method has to be provided
@ -515,8 +544,9 @@ public:
*/ */
using PackResult = typename PlacementStrategy::PackResult; using PackResult = typename PlacementStrategy::PackResult;
using ItemRef = std::reference_wrapper<Item>; using ItemRef = _ItemRef<Item>;
using ItemGroup = std::vector<ItemRef>; using ItemGroup = _ItemGroup<Item>;
using DefaultIterator = typename ItemGroup::const_iterator;
/** /**
* @brief Constructor taking the bin and an optional configuration. * @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 * Note that it depends on the particular placer implementation how it
* reacts to config changes in the middle of a calculation. * 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); } inline void configure(const Config& config) { impl_.configure(config); }
/** /**
* @brief A method that tries to pack an item and returns an object * Try to pack an item with a result object that contains the packing
* describing the pack result. * information for later accepting it.
* *
* The result can be casted to bool and used as an argument to the accept * \param item_store A container of items that are intended to be packed
* method to accept a succesfully packed item. This way the next packing * later. Can be used by the placer to switch tactics. When it's knows that
* will consider the accepted item as well. The PackResult should carry the * many items will come a greedy strategy may not be the best.
* transformation info so that if the tried item is later modified or tried * \param from The iterator to the item from which the packing should start,
* multiple times, the result object should set it to the originally * including the pointed item
* determied position. An implementation can be found in the * \param count How many items should be packed. If the value is 1, than
* strategies::PlacerBoilerplate::PackResult class. * just the item pointed to by "from" argument should be packed.
*
* @param item Ithe item to be packed.
* @return The PackResult object that can be implicitly casted to bool.
*/ */
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. * If the pack result is a failure the method should ignore it.
* @param r The result of a previous trypack call. * @param r The result of a previous trypack call.
@ -566,19 +599,25 @@ public:
inline void accept(PackResult& r) { impl_.accept(r); } 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 * 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 * 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. * in the implementation.
* *
* @param item The item to pack. * @param item The item to pack.
* @return Returns true if the item was packed or false if it could not be * @return Returns true if the item was packed or false if it could not be
* packed. * 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). /// Unpack the last element (remove it from the list of packed items).
inline void unpackLast() { impl_.unpackLast(); } inline void unpackLast() { impl_.unpackLast(); }
@ -597,13 +636,6 @@ public:
inline double filledArea() const { return impl_.filledArea(); } 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 // 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 * Note that it depends on the particular placer implementation how it
* reacts to config changes in the middle of a calculation. * 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) { inline void configure(const Config& config) {
impl_.configure(config); impl_.configure(config);
} }
/** /**
* @brief A function callback which should be called whenewer an item or * @brief A function callback which should be called whenever an item or
* a group of items where succesfully packed. * a group of items where successfully packed.
* @param fn A function callback object taking one unsigned integer as the * @param fn A function callback object taking one unsigned integer as the
* number of the remaining items to pack. * number of the remaining items to pack.
*/ */
@ -649,7 +681,7 @@ public:
* placer compatible with the PlacementStrategyLike interface. * placer compatible with the PlacementStrategyLike interface.
* *
* \param first, last The first and last iterator if the input sequence. It * \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 * \param bin. The shape of the bin. It has to be supported by the placement
* strategy. * strategy.
* \param An optional config object for the placer. * \param An optional config object for the placer.
@ -681,7 +713,7 @@ public:
/** /**
* @brief Get the items for a particular bin. * @brief Get the items for a particular bin.
* @param binIndex The index of the requested 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) { inline ItemGroup itemsForBin(size_t binIndex) {
return impl_.itemsForBin(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 * input items and outputs the items with the proper transformations to be
* inside the provided bin. * inside the provided bin.
*/ */
template<class PlacementStrategy, class SelectionStrategy > template<class PlacementStrategy, class SelectionStrategy >
class Arranger { class Nester {
using TSel = SelectionStrategyLike<SelectionStrategy>; using TSel = SelectionStrategyLike<SelectionStrategy>;
TSel selector_; TSel selector_;
bool use_min_bb_rotation_ = false;
public: public:
using Item = typename PlacementStrategy::Item; using Item = typename PlacementStrategy::Item;
using ItemRef = std::reference_wrapper<Item>; using ItemRef = std::reference_wrapper<Item>;
@ -769,7 +800,7 @@ public:
template<class TBinType = BinType, template<class TBinType = BinType,
class PConf = PlacementConfig, class PConf = PlacementConfig,
class SConf = SelectionConfig> class SConf = SelectionConfig>
Arranger( TBinType&& bin, Nester( TBinType&& bin,
Unit min_obj_distance = 0, Unit min_obj_distance = 0,
PConf&& pconfig = PConf(), PConf&& pconfig = PConf(),
SConf&& sconfig = SConf()): SConf&& sconfig = SConf()):
@ -802,9 +833,9 @@ public:
* the selection algorithm. * the selection algorithm.
*/ */
template<class TIterator> 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. * input sequence size.
*/ */
template<class TIterator> 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. /// Shorthand to normal arrange method.
template<class TIterator> template<class TIterator>
inline PackGroup operator() (TIterator from, TIterator to) inline PackGroup operator() (TIterator from, TIterator to)
{ {
return _arrange(from, to); return _execute(from, to);
} }
/// Set a progress indicatior function object for the selector. /// Set a progress indicator function object for the selector.
inline Arranger& progressIndicator(ProgressFunction func) inline Nester& progressIndicator(ProgressFunction func)
{ {
selector_.progressIndicator(func); return *this; selector_.progressIndicator(func); return *this;
} }
@ -842,24 +873,20 @@ public:
return ret; return ret;
} }
inline Arranger& useMinimumBoundigBoxRotation(bool s = true) {
use_min_bb_rotation_ = s; return *this;
}
private: private:
template<class TIterator, template<class TIterator,
class IT = remove_cvref_t<typename TIterator::value_type>, class IT = remove_cvref_t<typename TIterator::value_type>,
// This funtion will be used only if the iterators are pointing to // This function will be used only if the iterators are pointing to
// a type compatible with the binpack2d::_Item template. // a type compatible with the libnets2d::_Item template.
// This way we can use references to input elements as they will // This way we can use references to input elements as they will
// have to exist for the lifetime of this call. // have to exist for the lifetime of this call.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT> 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(); return lastResult();
} }
@ -867,28 +894,28 @@ private:
class IT = remove_cvref_t<typename TIterator::value_type>, class IT = remove_cvref_t<typename TIterator::value_type>,
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT> 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}; item_cache_ = {from, to};
__arrange(item_cache_.begin(), item_cache_.end()); __execute(item_cache_.begin(), item_cache_.end());
return lastResult(); return lastResult();
} }
template<class TIterator, template<class TIterator,
class IT = remove_cvref_t<typename TIterator::value_type>, class IT = remove_cvref_t<typename TIterator::value_type>,
// This funtion will be used only if the iterators are pointing to // This function will be used only if the iterators are pointing to
// a type compatible with the binpack2d::_Item template. // a type compatible with the libnest2d::_Item template.
// This way we can use references to input elements as they will // This way we can use references to input elements as they will
// have to exist for the lifetime of this call. // have to exist for the lifetime of this call.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT> class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
> >
inline IndexedPackGroup _arrangeIndexed(TIterator from, inline IndexedPackGroup _executeIndexed(TIterator from,
TIterator to, TIterator to,
bool = false) bool = false)
{ {
__arrange(from, to); __execute(from, to);
return createIndexedPackGroup(from, to, selector_); return createIndexedPackGroup(from, to, selector_);
} }
@ -896,12 +923,12 @@ private:
class IT = remove_cvref_t<typename TIterator::value_type>, class IT = remove_cvref_t<typename TIterator::value_type>,
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT> class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
> >
inline IndexedPackGroup _arrangeIndexed(TIterator from, inline IndexedPackGroup _executeIndexed(TIterator from,
TIterator to, TIterator to,
int = false) int = false)
{ {
item_cache_ = {from, to}; item_cache_ = {from, to};
__arrange(item_cache_.begin(), item_cache_.end()); __execute(item_cache_.begin(), item_cache_.end());
return createIndexedPackGroup(from, to, selector_); return createIndexedPackGroup(from, to, selector_);
} }
@ -933,37 +960,12 @@ private:
return pg; return pg;
} }
Radians findBestRotation(Item& item) { template<class TIter> inline void __execute(TIter from, TIter to)
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)
{ {
if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { 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))); 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>( selector_.template packItems<PlacementStrategy>(
from, to, bin_, pconfig_); from, to, bin_, pconfig_);

View file

@ -67,11 +67,11 @@ class metaloop {
// need to wrap that in a type (see metaloop::Int). // 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 * 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 * 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 * _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>; 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 // 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 // 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 // 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)) {} inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
template<class T> void operator ()(T&& pack_element) { template<class T> void operator ()(T&& pack_element) {
@ -146,7 +146,7 @@ public:
* version of run is called which does not call itself anymore. * version of run is called which does not call itself anymore.
* *
* If you are utterly annoyed, at least you have learned a super crazy * If you are utterly annoyed, at least you have learned a super crazy
* functional metaprogramming pattern. * functional meta-programming pattern.
*/ */
template<class...Args> template<class...Args>
using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>; using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;

View file

@ -102,6 +102,9 @@ struct StopCriteria {
/// If the relative value difference between two scores. /// If the relative value difference between two scores.
double relative_score_difference = std::nan(""); double relative_score_difference = std::nan("");
/// Stop if this value or better is found.
double stop_score = std::nan("");
unsigned max_iterations = 0; unsigned max_iterations = 0;
}; };

View file

@ -142,10 +142,12 @@ protected:
default: ; default: ;
} }
auto abs_diff = stopcr_.absolute_score_difference; double abs_diff = stopcr_.absolute_score_difference;
auto rel_diff = stopcr_.relative_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(abs_diff)) opt_.set_ftol_abs(abs_diff);
if(!std::isnan(rel_diff)) opt_.set_ftol_rel(rel_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) if(this->stopcr_.max_iterations > 0)
opt_.set_maxeval(this->stopcr_.max_iterations ); opt_.set_maxeval(this->stopcr_.max_iterations );

View file

@ -5,11 +5,26 @@
#include "placer_boilerplate.hpp" #include "placer_boilerplate.hpp"
namespace libnest2d { namespace strategies { namespace libnest2d { namespace placers {
template<class T, class = T> struct Epsilon {};
template<class T>
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> template<class RawShape>
struct BLConfig { 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; bool allow_rotations = false;
}; };
@ -27,9 +42,13 @@ public:
explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {} 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); auto r = _trypack(item);
if(!r && Base::config_.allow_rotations) { if(!r && Base::config_.allow_rotations) {
item.rotate(Degrees(90)); item.rotate(Degrees(90));
r =_trypack(item); r =_trypack(item);
} }
@ -65,20 +84,21 @@ protected:
setInitialPosition(item); setInitialPosition(item);
Unit d = availableSpaceDown(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 can_be_packed = can_move;
bool left = true; bool left = true;
while(can_move) { while(can_move) {
if(left) { // write previous down move and go down if(left) { // write previous down move and go down
item.translate({0, -d+1}); item.translate({0, -d+eps});
d = availableSpaceLeft(item); d = availableSpaceLeft(item);
can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/; can_move = d > eps;
left = false; left = false;
} else { // write previous left move and go down } else { // write previous left move and go down
item.translate({-d+1, 0}); item.translate({-d+eps, 0});
d = availableSpaceDown(item); d = availableSpaceDown(item);
can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/; can_move = d > eps;
left = true; left = true;
} }
} }
@ -112,10 +132,10 @@ protected:
const RawShape& scanpoly) const RawShape& scanpoly)
{ {
auto tsh = other.transformedShape(); auto tsh = other.transformedShape();
return ( ShapeLike::intersects(tsh, scanpoly) || return ( sl::intersects(tsh, scanpoly) ||
ShapeLike::isInside(tsh, scanpoly) ) && sl::isInside(tsh, scanpoly) ) &&
( !ShapeLike::intersects(tsh, item.rawShape()) && ( !sl::intersects(tsh, item.rawShape()) &&
!ShapeLike::isInside(tsh, item.rawShape()) ); !sl::isInside(tsh, item.rawShape()) );
} }
template<class C = Coord> template<class C = Coord>
@ -126,25 +146,25 @@ protected:
{ {
auto tsh = other.transformedShape(); auto tsh = other.transformedShape();
bool inters_scanpoly = ShapeLike::intersects(tsh, scanpoly) && bool inters_scanpoly = sl::intersects(tsh, scanpoly) &&
!ShapeLike::touches(tsh, scanpoly); !sl::touches(tsh, scanpoly);
bool inters_item = ShapeLike::intersects(tsh, item.rawShape()) && bool inters_item = sl::intersects(tsh, item.rawShape()) &&
!ShapeLike::touches(tsh, item.rawShape()); !sl::touches(tsh, item.rawShape());
return ( inters_scanpoly || return ( inters_scanpoly ||
ShapeLike::isInside(tsh, scanpoly)) && sl::isInside(tsh, scanpoly)) &&
( !inters_item && ( !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 // Get the left or down polygon, that has the same area as the shadow
// of input item reflected to the left or downwards // of input item reflected to the left or downwards
auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) : auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) :
downPoly(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()); ret.reserve(items_.size());
// Predicate to find items that are 'in the way' for left (down) move // Predicate to find items that are 'in the way' for left (down) move
@ -173,18 +193,18 @@ protected:
if(dir == Dir::LEFT) { if(dir == Dir::LEFT) {
getCoord = [](const Vertex& v) { return getX(v); }; getCoord = [](const Vertex& v) { return getX(v); };
availableDistance = PointLike::horizontalDistance<Vertex>; availableDistance = pointlike::horizontalDistance<Vertex>;
availableDistanceSV = [](const Segment& s, const Vertex& v) { 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; if(ret.second) ret.first = -ret.first;
return ret; return ret;
}; };
} }
else { else {
getCoord = [](const Vertex& v) { return getY(v); }; getCoord = [](const Vertex& v) { return getY(v); };
availableDistance = PointLike::verticalDistance<Vertex>; availableDistance = pointlike::verticalDistance<Vertex>;
availableDistanceSV = [](const Segment& s, const Vertex& v) { 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; if(ret.second) ret.first = -ret.first;
return ret; return ret;
}; };
@ -214,9 +234,9 @@ protected:
assert(pleft.vertexCount() > 0); assert(pleft.vertexCount() > 0);
auto trpleft = pleft.transformedShape(); auto trpleft = pleft.transformedShape();
auto first = ShapeLike::begin(trpleft); auto first = sl::begin(trpleft);
auto next = first + 1; auto next = first + 1;
auto endit = ShapeLike::end(trpleft); auto endit = sl::end(trpleft);
while(next != endit) { while(next != endit) {
Segment seg(*(first++), *(next++)); Segment seg(*(first++), *(next++));
@ -340,16 +360,16 @@ protected:
// reserve for all vertices plus 2 for the left horizontal wall, 2 for // reserve for all vertices plus 2 for the left horizontal wall, 2 for
// the additional vertices for maintaning min object distance // 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](){ /*auto addOthers = [&rsh, finish, start, &item](){
for(size_t i = start+1; i < finish; i++) 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](){ auto reverseAddOthers = [&rsh, finish, start, &item](){
for(auto i = finish-1; i > start; i--) for(auto i = finish-1; i > start; i--)
ShapeLike::addVertex(rsh, item.vertex( sl::addVertex(rsh, item.vertex(
static_cast<unsigned long>(i))); static_cast<unsigned long>(i)));
}; };
@ -361,25 +381,25 @@ protected:
// Clockwise polygon construction // Clockwise polygon construction
ShapeLike::addVertex(rsh, topleft_vertex); sl::addVertex(rsh, topleft_vertex);
if(dir == Dir::LEFT) reverseAddOthers(); if(dir == Dir::LEFT) reverseAddOthers();
else { else {
ShapeLike::addVertex(rsh, getX(topleft_vertex), 0); sl::addVertex(rsh, getX(topleft_vertex), 0);
ShapeLike::addVertex(rsh, getX(bottomleft_vertex), 0); sl::addVertex(rsh, getX(bottomleft_vertex), 0);
} }
ShapeLike::addVertex(rsh, bottomleft_vertex); sl::addVertex(rsh, bottomleft_vertex);
if(dir == Dir::LEFT) { if(dir == Dir::LEFT) {
ShapeLike::addVertex(rsh, 0, getY(bottomleft_vertex)); sl::addVertex(rsh, 0, getY(bottomleft_vertex));
ShapeLike::addVertex(rsh, 0, getY(topleft_vertex)); sl::addVertex(rsh, 0, getY(topleft_vertex));
} }
else reverseAddOthers(); else reverseAddOthers();
// Close the polygon // Close the polygon
ShapeLike::addVertex(rsh, topleft_vertex); sl::addVertex(rsh, topleft_vertex);
return rsh; return rsh;
} }

File diff suppressed because it is too large Load diff

View file

@ -3,14 +3,11 @@
#include "../libnest2d.hpp" #include "../libnest2d.hpp"
namespace libnest2d { namespace strategies { namespace libnest2d { namespace placers {
struct EmptyConfig {}; struct EmptyConfig {};
template<class Subclass, class RawShape, class TBin, template<class Subclass, class RawShape, class TBin, class Cfg = EmptyConfig>
class Cfg = EmptyConfig,
class Store = std::vector<std::reference_wrapper<_Item<RawShape>>>
>
class PlacerBoilerplate { class PlacerBoilerplate {
mutable bool farea_valid_ = false; mutable bool farea_valid_ = false;
mutable double farea_ = 0.0; mutable double farea_ = 0.0;
@ -22,25 +19,30 @@ public:
using Coord = TCoord<Vertex>; using Coord = TCoord<Vertex>;
using Unit = Coord; using Unit = Coord;
using Config = Cfg; using Config = Cfg;
using Container = Store; using ItemGroup = _ItemGroup<Item>;
using DefaultIter = typename ItemGroup::const_iterator;
class PackResult { class PackResult {
Item *item_ptr_; Item *item_ptr_;
Vertex move_; Vertex move_;
Radians rot_; Radians rot_;
double overfit_;
friend class PlacerBoilerplate; friend class PlacerBoilerplate;
friend Subclass; friend Subclass;
PackResult(Item& item): PackResult(Item& item):
item_ptr_(&item), item_ptr_(&item),
move_(item.translation()), move_(item.translation()),
rot_(item.rotation()) {} rot_(item.rotation()) {}
PackResult(): item_ptr_(nullptr) {}
PackResult(double overfit = 1.0):
item_ptr_(nullptr), overfit_(overfit) {}
public: public:
operator bool() { return item_ptr_ != nullptr; } 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) inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin)
{ {
items_.reserve(cap); items_.reserve(cap);
@ -56,8 +58,10 @@ public:
config_ = config; config_ = config;
} }
bool pack(Item& item) { template<class Range = ConstItemRange<DefaultIter>>
auto&& r = static_cast<Subclass*>(this)->trypack(item); bool pack(Item& item,
const Range& rem = Range()) {
auto&& r = static_cast<Subclass*>(this)->trypack(item, rem);
if(r) { if(r) {
items_.push_back(*(r.item_ptr_)); items_.push_back(*(r.item_ptr_));
farea_valid_ = false; farea_valid_ = false;
@ -79,14 +83,11 @@ public:
farea_valid_ = false; farea_valid_ = false;
} }
inline ItemGroup getItems() const { return items_; } inline const ItemGroup& getItems() const { return items_; }
inline void clearItems() { inline void clearItems() {
items_.clear(); items_.clear();
farea_valid_ = false; farea_valid_ = false;
#ifndef NDEBUG
debug_items_.clear();
#endif
} }
inline double filledArea() const { inline double filledArea() const {
@ -103,14 +104,10 @@ public:
return farea_; return farea_;
} }
#ifndef NDEBUG
std::vector<Item> debug_items_;
#endif
protected: protected:
BinType bin_; BinType bin_;
Container items_; ItemGroup items_;
Cfg config_; Cfg config_;
}; };
@ -121,6 +118,7 @@ using Base::items_; \
using Base::config_; \ using Base::config_; \
public: \ public: \
using typename Base::Item; \ using typename Base::Item; \
using typename Base::ItemGroup; \
using typename Base::BinType; \ using typename Base::BinType; \
using typename Base::Config; \ using typename Base::Config; \
using typename Base::Vertex; \ using typename Base::Vertex; \
@ -128,7 +126,6 @@ using typename Base::Segment; \
using typename Base::PackResult; \ using typename Base::PackResult; \
using typename Base::Coord; \ using typename Base::Coord; \
using typename Base::Unit; \ using typename Base::Unit; \
using typename Base::Container; \
private: private:
} }

View 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

View file

@ -8,7 +8,7 @@
#include "selection_boilerplate.hpp" #include "selection_boilerplate.hpp"
namespace libnest2d { namespace strategies { namespace libnest2d { namespace selections {
/** /**
* Selection heuristic based on [López-Camacho]\ * Selection heuristic based on [López-Camacho]\
@ -118,7 +118,7 @@ public:
using Placer = PlacementStrategyLike<TPlacer>; using Placer = PlacementStrategyLike<TPlacer>;
using ItemList = std::list<ItemRef>; 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 w = bin_area * config_.waste_increment;
const double INITIAL_FILL_PROPORTION = config_.initial_fill_proportion; const double INITIAL_FILL_PROPORTION = config_.initial_fill_proportion;
@ -227,10 +227,14 @@ public:
bool ret = false; bool ret = false;
auto it = not_packed.begin(); auto it = not_packed.begin();
auto pack = [&placer, &not_packed](ItemListIt it) {
return placer.pack(*it, rem(it, not_packed));
};
while(it != not_packed.end() && !ret && while(it != not_packed.end() && !ret &&
free_area - (item_area = it->get().area()) <= waste) 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; free_area -= item_area;
filled_area = bin_area - free_area; filled_area = bin_area - free_area;
ret = true; ret = true;
@ -270,6 +274,11 @@ public:
auto it2 = it; auto it2 = it;
std::vector<TPair> wrong_pairs; std::vector<TPair> wrong_pairs;
using std::placeholders::_1;
auto trypack = [&placer, &not_packed](ItemListIt it) {
return placer.trypack(*it, rem(it, not_packed));
};
while(it != endit && !ret && while(it != endit && !ret &&
free_area - (item_area = it->get().area()) - free_area - (item_area = it->get().area()) -
@ -278,7 +287,7 @@ public:
if(item_area + smallestPiece(it, not_packed)->get().area() > if(item_area + smallestPiece(it, not_packed)->get().area() >
free_area ) { it++; continue; } free_area ) { it++; continue; }
auto pr = placer.trypack(*it); auto pr = trypack(it);
// First would fit // First would fit
it2 = not_packed.begin(); it2 = not_packed.begin();
@ -294,14 +303,14 @@ public:
} }
placer.accept(pr); placer.accept(pr);
auto pr2 = placer.trypack(*it2); auto pr2 = trypack(it2);
if(!pr2) { if(!pr2) {
placer.unpackLast(); // remove first placer.unpackLast(); // remove first
if(try_reverse) { if(try_reverse) {
pr2 = placer.trypack(*it2); pr2 = trypack(it2);
if(pr2) { if(pr2) {
placer.accept(pr2); placer.accept(pr2);
auto pr12 = placer.trypack(*it); auto pr12 = trypack(it);
if(pr12) { if(pr12) {
placer.accept(pr12); placer.accept(pr12);
ret = true; ret = true;
@ -365,6 +374,14 @@ public:
return it->get().area(); return it->get().area();
}; };
auto trypack = [&placer, &not_packed](ItemListIt it) {
return placer.trypack(*it, rem(it, not_packed));
};
auto pack = [&placer, &not_packed](ItemListIt it) {
return placer.pack(*it, rem(it, not_packed));
};
while (it != endit && !ret) { // drill down 1st level while (it != endit && !ret) { // drill down 1st level
// We need to determine in each iteration the largest, second // We need to determine in each iteration the largest, second
@ -394,7 +411,7 @@ public:
it++; continue; it++; continue;
} }
auto pr = placer.trypack(*it); auto pr = trypack(it);
// Check for free area and try to pack the 1st item... // Check for free area and try to pack the 1st item...
if(!pr) { it++; continue; } if(!pr) { it++; continue; }
@ -420,15 +437,15 @@ public:
bool can_pack2 = false; bool can_pack2 = false;
placer.accept(pr); placer.accept(pr);
auto pr2 = placer.trypack(*it2); auto pr2 = trypack(it2);
auto pr12 = pr; auto pr12 = pr;
if(!pr2) { if(!pr2) {
placer.unpackLast(); // remove first placer.unpackLast(); // remove first
if(try_reverse) { if(try_reverse) {
pr2 = placer.trypack(*it2); pr2 = trypack(it2);
if(pr2) { if(pr2) {
placer.accept(pr2); placer.accept(pr2);
pr12 = placer.trypack(*it); pr12 = trypack(it);
if(pr12) can_pack2 = true; if(pr12) can_pack2 = true;
placer.unpackLast(); placer.unpackLast();
} }
@ -463,7 +480,7 @@ public:
if(a3_sum > free_area) { it3++; continue; } if(a3_sum > free_area) { it3++; continue; }
placer.accept(pr12); placer.accept(pr2); placer.accept(pr12); placer.accept(pr2);
bool can_pack3 = placer.pack(*it3); bool can_pack3 = pack(it3);
if(!can_pack3) { if(!can_pack3) {
placer.unpackLast(); placer.unpackLast();
@ -473,16 +490,16 @@ public:
if(!can_pack3 && try_reverse) { if(!can_pack3 && try_reverse) {
std::array<size_t, 3> indices = {0, 1, 2}; std::array<size_t, 3> indices = {0, 1, 2};
std::array<ItemRef, 3> std::array<typename ItemList::iterator, 3>
candidates = {*it, *it2, *it3}; candidates = {it, it2, it3};
auto tryPack = [&placer, &candidates]( auto tryPack = [&placer, &candidates, &pack](
const decltype(indices)& idx) const decltype(indices)& idx)
{ {
std::array<bool, 3> packed = {false}; std::array<bool, 3> packed = {false};
for(auto id : idx) packed.at(id) = for(auto id : idx) packed.at(id) =
placer.pack(candidates[id]); pack(candidates[id]);
bool check = bool check =
std::all_of(packed.begin(), std::all_of(packed.begin(),
@ -536,7 +553,7 @@ public:
{ auto it = store_.begin(); { auto it = store_.begin();
while (it != store_.end()) { while (it != store_.end()) {
Placer p(bin); p.configure(pconfig); Placer p(bin); p.configure(pconfig);
if(!p.pack(*it)) { if(!p.pack(*it, rem(it, store_))) {
it = store_.erase(it); it = store_.erase(it);
} else it++; } else it++;
} }
@ -551,11 +568,7 @@ public:
{ {
packed_bins_[idx] = placer.getItems(); 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 // TODO here should be a spinlock
slock.lock(); slock.lock();
acounter -= packednum; acounter -= packednum;
@ -601,7 +614,7 @@ public:
while(it != not_packed.end() && while(it != not_packed.end() &&
filled_area < INITIAL_FILL_AREA) filled_area < INITIAL_FILL_AREA)
{ {
if(placer.pack(*it)) { if(placer.pack(*it, rem(it, not_packed))) {
filled_area += it->get().area(); filled_area += it->get().area();
free_area = bin_area - filled_area; free_area = bin_area - filled_area;
it = not_packed.erase(it); it = not_packed.erase(it);

View file

@ -3,7 +3,7 @@
#include "selection_boilerplate.hpp" #include "selection_boilerplate.hpp"
namespace libnest2d { namespace strategies { namespace libnest2d { namespace selections {
template<class RawShape> template<class RawShape>
class _FillerSelection: public SelectionBoilerplate<RawShape> { class _FillerSelection: public SelectionBoilerplate<RawShape> {
@ -56,18 +56,13 @@ public:
std::sort(store_.begin(), store_.end(), sortfunc); 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); PlacementStrategyLike<TPlacer> placer(bin);
placer.configure(pconfig); placer.configure(pconfig);
auto it = store_.begin(); auto it = store_.begin();
while(it != store_.end()) { while(it != store_.end()) {
if(!placer.pack(*it)) { if(!placer.pack(*it, {std::next(it), store_.end()})) {
if(packed_bins_.back().empty()) ++it; if(packed_bins_.back().empty()) ++it;
// makeProgress(placer);
placer.clearItems(); placer.clearItems();
packed_bins_.emplace_back(); packed_bins_.emplace_back();
} else { } else {
@ -76,9 +71,6 @@ public:
} }
} }
// if(was_packed) {
// packed_bins_.push_back(placer.getItems());
// }
} }
}; };

View file

@ -4,7 +4,7 @@
#include "../libnest2d.hpp" #include "../libnest2d.hpp"
#include "selection_boilerplate.hpp" #include "selection_boilerplate.hpp"
namespace libnest2d { namespace strategies { namespace libnest2d { namespace selections {
template<class RawShape> template<class RawShape>
class _FirstFitSelection: public SelectionBoilerplate<RawShape> { class _FirstFitSelection: public SelectionBoilerplate<RawShape> {
@ -40,6 +40,7 @@ public:
packed_bins_.clear(); packed_bins_.clear();
std::vector<Placer> placers; std::vector<Placer> placers;
placers.reserve(last-first);
std::copy(first, last, std::back_inserter(store_)); std::copy(first, last, std::back_inserter(store_));
@ -66,21 +67,25 @@ public:
} }
} }
for(auto& item : store_ ) { auto it = store_.begin();
bool was_packed = false;
while(!was_packed) {
for(size_t j = 0; j < placers.size() && !was_packed; j++) { while(it != store_.end()) {
if((was_packed = placers[j].pack(item))) bool was_packed = false;
makeProgress(placers[j], j); 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) { if(!was_packed) {
placers.emplace_back(bin); placers.emplace_back(bin);
placers.back().configure(pconfig); placers.back().configure(pconfig);
packed_bins_.emplace_back(); packed_bins_.emplace_back();
j = placers.size() - 1;
} }
} }
++it;
} }
} }

View file

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

View file

@ -5,6 +5,7 @@
#include "printer_parts.h" #include "printer_parts.h"
#include <libnest2d/geometry_traits_nfp.hpp> #include <libnest2d/geometry_traits_nfp.hpp>
//#include "../tools/libnfpglue.hpp" //#include "../tools/libnfpglue.hpp"
//#include "../tools/nfp_svgnest_glue.hpp"
std::vector<libnest2d::Item>& prusaParts() { std::vector<libnest2d::Item>& prusaParts() {
static std::vector<libnest2d::Item> ret; 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) { TEST(GeometryAlgorithms, Distance) {
using namespace libnest2d; using namespace libnest2d;
@ -107,14 +145,14 @@ TEST(GeometryAlgorithms, Distance) {
Point p2 = {10, 0}; Point p2 = {10, 0};
Point p3 = {10, 10}; Point p3 = {10, 10};
ASSERT_DOUBLE_EQ(PointLike::distance(p1, p2), 10); ASSERT_DOUBLE_EQ(pointlike::distance(p1, p2), 10);
ASSERT_DOUBLE_EQ(PointLike::distance(p1, p3), sqrt(200)); ASSERT_DOUBLE_EQ(pointlike::distance(p1, p3), sqrt(200));
Segment seg(p1, p3); 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) { auto check = [](Coord val, Coord expected) {
if(std::is_floating_point<Coord>::value) if(std::is_floating_point<Coord>::value)
@ -127,11 +165,11 @@ TEST(GeometryAlgorithms, Distance) {
ASSERT_TRUE(result.second); ASSERT_TRUE(result.second);
check(result.first, 10); check(result.first, 10);
result = PointLike::verticalDistance(p2, seg); result = pointlike::verticalDistance(p2, seg);
ASSERT_TRUE(result.second); ASSERT_TRUE(result.second);
check(result.first, -10); check(result.first, -10);
result = PointLike::verticalDistance(Point{10, 20}, seg); result = pointlike::verticalDistance(Point{10, 20}, seg);
ASSERT_TRUE(result.second); ASSERT_TRUE(result.second);
check(result.first, 10); check(result.first, 10);
@ -139,12 +177,12 @@ TEST(GeometryAlgorithms, Distance) {
Point p4 = {80, 0}; Point p4 = {80, 0};
Segment seg2 = { {0, 0}, {0, 40} }; Segment seg2 = { {0, 0}, {0, 40} };
result = PointLike::horizontalDistance(p4, seg2); result = pointlike::horizontalDistance(p4, seg2);
ASSERT_TRUE(result.second); ASSERT_TRUE(result.second);
check(result.first, 80); check(result.first, 80);
result = PointLike::verticalDistance(p4, seg2); result = pointlike::verticalDistance(p4, seg2);
// Point should not be related to the segment // Point should not be related to the segment
ASSERT_FALSE(result.second); ASSERT_FALSE(result.second);
@ -172,7 +210,7 @@ TEST(GeometryAlgorithms, Area) {
{61, 97} {61, 97}
}; };
ASSERT_TRUE(ShapeLike::area(item.transformedShape()) > 0 ); ASSERT_TRUE(shapelike::area(item.transformedShape()) > 0 );
} }
TEST(GeometryAlgorithms, IsPointInsidePolygon) { TEST(GeometryAlgorithms, IsPointInsidePolygon) {
@ -182,21 +220,21 @@ TEST(GeometryAlgorithms, IsPointInsidePolygon) {
Point p = {1, 1}; Point p = {1, 1};
ASSERT_TRUE(rect.isPointInside(p)); ASSERT_TRUE(rect.isInside(p));
p = {11, 11}; p = {11, 11};
ASSERT_FALSE(rect.isPointInside(p)); ASSERT_FALSE(rect.isInside(p));
p = {11, 12}; p = {11, 12};
ASSERT_FALSE(rect.isPointInside(p)); ASSERT_FALSE(rect.isInside(p));
p = {3, 3}; 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)); 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()); ASSERT_EQ(leftp.vertexCount(), leftControl.vertexCount());
for(unsigned long i = 0; i < leftControl.vertexCount(); i++) { for(unsigned long i = 0; i < leftControl.vertexCount(); i++) {
@ -260,7 +298,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon)
Item downp(placer.downPoly(item)); 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()); ASSERT_EQ(downp.vertexCount(), downControl.vertexCount());
for(unsigned long i = 0; i < downControl.vertexCount(); i++) { for(unsigned long i = 0; i < downControl.vertexCount(); i++) {
@ -297,7 +335,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight)
{20, 20} }; {20, 20} };
Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250)); Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250));
auto groups = arrange(rects.begin(), rects.end()); auto groups = arrange(rects.begin(), rects.end());
@ -350,7 +388,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose)
Coord min_obj_distance = 5; Coord min_obj_distance = 5;
Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250), Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250),
min_obj_distance); min_obj_distance);
auto groups = arrange(rects.begin(), rects.end()); 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); setX(v, getX(v)/SCALE);
rbin.setVertex(i, v); 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) { for(Item& sh : r) {
Item tsh(sh.transformedShape()); Item tsh(sh.transformedShape());
for(unsigned i = 0; i < tsh.vertexCount(); i++) { 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); setX(v, getX(v)/SCALE);
tsh.setVertex(i, v); 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; 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) { void testNfp(const std::vector<ItemPair>& testdata) {
using namespace libnest2d; using namespace libnest2d;
@ -674,29 +712,33 @@ void testNfp(const std::vector<ItemPair>& testdata) {
auto& exportfun = exportSVG<SCALE, Box>; auto& exportfun = exportSVG<SCALE, Box>;
auto onetest = [&](Item& orbiter, Item& stationary){ auto onetest = [&](Item& orbiter, Item& stationary, unsigned testidx){
testcase++; testcase++;
orbiter.translate({210*SCALE, 0}); orbiter.translate({210*SCALE, 0});
auto&& nfp = Nfp::noFitPolygon<lvl>(stationary.rawShape(), auto&& nfp = nfp::noFitPolygon<lvl>(stationary.rawShape(),
orbiter.transformedShape()); orbiter.transformedShape());
strategies::correctNfpPosition(nfp, stationary, orbiter); placers::correctNfpPosition(nfp, stationary, orbiter);
auto v = ShapeLike::isValid(nfp.first); auto valid = shapelike::isValid(nfp.first);
if(!v.first) { /*Item infp(nfp.first);
std::cout << v.second << std::endl; if(!valid.first) {
} std::cout << "test instance: " << testidx << " "
<< valid.second << std::endl;
std::vector<std::reference_wrapper<Item>> inp = {std::ref(infp)};
exportfun(inp, bin, testidx);
}*/
ASSERT_TRUE(v.first); ASSERT_TRUE(valid.first);
Item infp(nfp.first); Item infp(nfp.first);
int i = 0; int i = 0;
auto rorbiter = orbiter.transformedShape(); auto rorbiter = orbiter.transformedShape();
auto vo = Nfp::referenceVertex(rorbiter); auto vo = nfp::referenceVertex(rorbiter);
ASSERT_TRUE(stationary.isInside(infp)); ASSERT_TRUE(stationary.isInside(infp));
@ -710,7 +752,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
bool touching = Item::touches(tmp, stationary); bool touching = Item::touches(tmp, stationary);
if(!touching) { if(!touching || !valid.first) {
std::vector<std::reference_wrapper<Item>> inp = { std::vector<std::reference_wrapper<Item>> inp = {
std::ref(stationary), std::ref(tmp), std::ref(infp) std::ref(stationary), std::ref(tmp), std::ref(infp)
}; };
@ -722,22 +764,24 @@ void testNfp(const std::vector<ItemPair>& testdata) {
} }
}; };
unsigned tidx = 0;
for(auto& td : testdata) { for(auto& td : testdata) {
auto orbiter = td.orbiter; auto orbiter = td.orbiter;
auto stationary = td.stationary; auto stationary = td.stationary;
onetest(orbiter, stationary); onetest(orbiter, stationary, tidx++);
} }
tidx = 0;
for(auto& td : testdata) { for(auto& td : testdata) {
auto orbiter = td.stationary; auto orbiter = td.stationary;
auto stationary = td.orbiter; auto stationary = td.orbiter;
onetest(orbiter, stationary); onetest(orbiter, stationary, tidx++);
} }
} }
} }
TEST(GeometryAlgorithms, nfpConvexConvex) { TEST(GeometryAlgorithms, nfpConvexConvex) {
testNfp<NfpLevel::CONVEX_ONLY, 1>(nfp_testdata); testNfp<nfp::NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
} }
//TEST(GeometryAlgorithms, nfpConcaveConcave) { //TEST(GeometryAlgorithms, nfpConcaveConcave) {
@ -758,7 +802,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) {
Rectangle input(10, 10); Rectangle input(10, 10);
strategies::EdgeCache<PolygonImpl> ecache(input); placers::EdgeCache<PolygonImpl> ecache(input);
auto first = *input.begin(); auto first = *input.begin();
ASSERT_TRUE(getX(first) == getX(ecache.coords(0))); ASSERT_TRUE(getX(first) == getX(ecache.coords(0)));
@ -770,7 +814,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) {
for(int i = 0; i <= 100; i++) { for(int i = 0; i <= 100; i++) {
auto v = ecache.coords(i*(0.01)); 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}); rect2.translate({10, 0});
rect3.translate({25, 0}); rect3.translate({25, 0});
ShapeLike::Shapes<PolygonImpl> pile; shapelike::Shapes<PolygonImpl> pile;
pile.push_back(rect1.transformedShape()); pile.push_back(rect1.transformedShape());
pile.push_back(rect2.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); ASSERT_EQ(result.size(), 1);
Rectangle ref(45, 15); 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) { int main(int argc, char **argv) {

View file

@ -56,7 +56,7 @@ libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) {
NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
{ {
using Vertex = PointImpl; namespace sl = shapelike;
NfpR ret; NfpR ret;
@ -85,7 +85,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
// this can throw // this can throw
auto nfp = libnfporb::generateNFP(pstat, porb, true); 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); ct.reserve(nfp.front().size()+1);
for(auto v : nfp.front()) { for(auto v : nfp.front()) {
v = scale(v, refactor); v = scale(v, refactor);
@ -94,7 +94,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
ct.push_back(ct.front()); ct.push_back(ct.front());
std::reverse(ct.begin(), ct.end()); 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) { for(size_t hidx = 1; hidx < nfp.size(); ++hidx) {
if(nfp[hidx].size() >= 3) { if(nfp[hidx].size() >= 3) {
rholes.emplace_back(); 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) { } catch(std::exception& e) {
std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl; std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl;
// auto ch_stat = ShapeLike::convexHull(sh); // auto ch_stat = ShapeLike::convexHull(sh);
// auto ch_orb = ShapeLike::convexHull(cother); // auto ch_orb = ShapeLike::convexHull(cother);
ret = Nfp::nfpConvexOnly(sh, cother); ret = nfp::nfpConvexOnly(sh, cother);
} }
return ret; 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) const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{ {
return _nfp(sh, cother);//nfpConvexOnly(sh, 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) const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{ {
return _nfp(sh, 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) const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{ {
return _nfp(sh, cother); return _nfp(sh, cother);

View file

@ -5,22 +5,22 @@
namespace libnest2d { namespace libnest2d {
using NfpR = Nfp::NfpResult<PolygonImpl>; using NfpR = nfp::NfpResult<PolygonImpl>;
NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother); NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
template<> template<>
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> { struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
}; };
template<> template<>
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> { struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
}; };
template<> template<>
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> { struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); 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); // NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
//}; //};
template<> struct Nfp::MaxNfpLevel<PolygonImpl> { template<> struct nfp::MaxNfpLevel<PolygonImpl> {
static const BP2D_CONSTEXPR NfpLevel value = static const BP2D_CONSTEXPR NfpLevel value =
// NfpLevel::CONVEX_ONLY; // NfpLevel::CONVEX_ONLY;
NfpLevel::BOTH_CONCAVE; NfpLevel::BOTH_CONCAVE;

File diff suppressed because it is too large Load diff

View 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

View file

@ -56,14 +56,14 @@ public:
auto d = static_cast<Coord>( auto d = static_cast<Coord>(
std::round(conf_.height*conf_.mm_in_coord_units) ); 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); 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); 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"; 1.0/conf_.mm_in_coord_units) + "\n";
} }

View file

@ -99,54 +99,55 @@ namespace bgi = boost::geometry::index;
using SpatElement = std::pair<Box, unsigned>; using SpatElement = std::pair<Box, unsigned>;
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
using ItemGroup = std::vector<std::reference_wrapper<Item>>;
template<class TBin>
using TPacker = typename placers::_NofitPolyPlacer<PolygonImpl, TBin>;
const double BIG_ITEM_TRESHOLD = 0.02;
Box boundingBox(const Box& pilebb, const Box& ibb ) {
auto& pminc = pilebb.minCorner();
auto& pmaxc = pilebb.maxCorner();
auto& iminc = ibb.minCorner();
auto& imaxc = ibb.maxCorner();
PointImpl minc, maxc;
setX(minc, std::min(getX(pminc), getX(iminc)));
setY(minc, std::min(getY(pminc), getY(iminc)));
setX(maxc, std::max(getX(pmaxc), getX(imaxc)));
setY(maxc, std::max(getY(pmaxc), getY(imaxc)));
return Box(minc, maxc);
}
std::tuple<double /*score*/, Box /*farthest point from bin center*/> std::tuple<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const PointImpl& bincenter, objfunc(const PointImpl& bincenter,
double /*bin_area*/, const shapelike::Shapes<PolygonImpl>& merged_pile,
ShapeLike::Shapes<PolygonImpl>& pile, // The currently arranged pile const Box& pilebb,
double /*pile_area*/, const ItemGroup& items,
const Item &item, const Item &item,
double bin_area,
double norm, // A norming factor for physical dimensions double norm, // A norming factor for physical dimensions
std::vector<double>& areacache, // pile item areas will be cached
// a spatial index to quickly get neighbors of the candidate item // a spatial index to quickly get neighbors of the candidate item
SpatIndex& spatindex const SpatIndex& spatindex,
const ItemGroup& remaining
) )
{ {
using pl = PointLike; using Coord = TCoord<PointImpl>;
using sl = ShapeLike;
static const double BIG_ITEM_TRESHOLD = 0.2;
static const double ROUNDNESS_RATIO = 0.5; static const double ROUNDNESS_RATIO = 0.5;
static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO;
// We will treat big items (compared to the print bed) differently // We will treat big items (compared to the print bed) differently
auto normarea = [norm](double area) { return std::sqrt(area)/norm; }; auto isBig = [bin_area](double a) {
return a/bin_area > BIG_ITEM_TRESHOLD ;
// If a new bin has been created: };
if(pile.size() < areacache.size()) {
areacache.clear();
spatindex.clear();
}
// We must fill the caches:
int idx = 0;
for(auto& p : pile) {
if(idx == areacache.size()) {
areacache.emplace_back(sl::area(p));
if(normarea(areacache[idx]) > BIG_ITEM_TRESHOLD)
spatindex.insert({sl::boundingBox(p), idx});
}
idx++;
}
// Candidate item bounding box // Candidate item bounding box
auto ibb = item.boundingBox(); auto ibb = sl::boundingBox(item.transformedShape());
// Calculate the full bounding box of the pile with the candidate item // Calculate the full bounding box of the pile with the candidate item
pile.emplace_back(item.transformedShape()); auto fullbb = boundingBox(pilebb, ibb);
auto fullbb = ShapeLike::boundingBox(pile);
pile.pop_back();
// The bounding box of the big items (they will accumulate in the center // The bounding box of the big items (they will accumulate in the center
// of the pile // of the pile
@ -157,19 +158,11 @@ objfunc(const PointImpl& bincenter,
boost::geometry::convert(boostbb, bigbb); 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 // Will hold the resulting score
double score = 0; double score = 0;
if(item_normarea > BIG_ITEM_TRESHOLD) { if(isBig(item.area())) {
// This branch is for the bigger items.. // 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 minc = ibb.minCorner(); // bottom left corner
auto maxc = ibb.maxCorner(); // top right 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; auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
// Density is the pack density: how big is the arranged pile // 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. if(remaining.empty()) {
// 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();
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 double circ = ec.circumference() / norm;
std::vector<SpatElement> result; double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm;
spatindex.query(bgi::intersects(querybb), std::back_inserter(result)); score = 0.5*circ + 0.5*bcirc;
for(auto& e : result) { // now get the score for the best alignment } else {
auto idx = e.second; // Prepare a variable for the alignment score.
auto& p = pile[idx]; // This will indicate: how well is the candidate item aligned with
auto parea = areacache[idx]; // its neighbors. We will check the alignment with all neighbors and
auto bb = sl::boundingBox(sl::Shapes<PolygonImpl>{p, trsh}); // return the score for the best alignment. So it is enough for the
auto bbarea = bb.area(); // candidate to be aligned with only one item.
auto ascore = 1.0 - (item.area() + parea)/bbarea; 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;
} }
} else if( !isBig(item.area()) && spatindex.empty()) {
// 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
auto bindist = pl::distance(ibb.center(), bincenter) / norm; auto bindist = pl::distance(ibb.center(), bincenter) / norm;
auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; // Bindist is surprisingly enough...
score = ROUNDNESS_RATIO * bindist + DENSITY_RATIO * density; score = bindist;
} else { } else {
// Here there are the small items that should be placed around the // Here there are the small items that should be placed around the
// already processed bigger items. // already processed bigger items.
@ -259,7 +268,9 @@ void fillConfig(PConf& pcfg) {
// The accuracy of optimization. // The accuracy of optimization.
// Goes from 0.0 to 1.0 and scales performance as well // Goes from 0.0 to 1.0 and scales performance as well
pcfg.accuracy = 0.6f; pcfg.accuracy = 0.65f;
pcfg.parallel = true;
} }
template<class TBin> template<class TBin>
@ -268,31 +279,62 @@ class AutoArranger {};
template<class TBin> template<class TBin>
class _ArrBase { class _ArrBase {
protected: protected:
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>;
using Placer = TPacker<TBin>;
using Selector = FirstFitSelection; using Selector = FirstFitSelection;
using Packer = Arranger<Placer, Selector>; using Packer = Nester<Placer, Selector>;
using PConfig = typename Packer::PlacementConfig; using PConfig = typename Packer::PlacementConfig;
using Distance = TCoord<PointImpl>; using Distance = TCoord<PointImpl>;
using Pile = ShapeLike::Shapes<PolygonImpl>; using Pile = sl::Shapes<PolygonImpl>;
Packer pck_; Packer pck_;
PConfig pconf_; // Placement configuration PConfig pconf_; // Placement configuration
double bin_area_; double bin_area_;
std::vector<double> areacache_;
SpatIndex rtree_; SpatIndex rtree_;
double norm_;
Pile merged_pile_;
Box pilebb_;
ItemGroup remaining_;
ItemGroup items_;
public: public:
_ArrBase(const TBin& bin, Distance dist, _ArrBase(const TBin& bin, Distance dist,
std::function<void(unsigned)> progressind): 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_); fillConfig(pconf_);
pconf_.before_packing =
[this](const Pile& merged_pile, // merged pile
const ItemGroup& items, // packed items
const ItemGroup& remaining) // future items to be packed
{
items_ = items;
merged_pile_ = merged_pile;
remaining_ = remaining;
pilebb_ = sl::boundingBox(merged_pile);
rtree_.clear();
// We will treat big items (compared to the print bed) differently
auto isBig = [this](double a) {
return a/bin_area_ > BIG_ITEM_TRESHOLD ;
};
for(unsigned idx = 0; idx < items.size(); ++idx) {
Item& itm = items[idx];
if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
}
};
pck_.progressIndicator(progressind); pck_.progressIndicator(progressind);
} }
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) { template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
areacache_.clear(); rtree_.clear();
return pck_.arrangeIndexed(std::forward<Args>(args)...); return pck_.executeIndexed(std::forward<Args>(args)...);
} }
}; };
@ -304,22 +346,69 @@ public:
std::function<void(unsigned)> progressind): std::function<void(unsigned)> progressind):
_ArrBase<Box>(bin, dist, progressind) _ArrBase<Box>(bin, dist, progressind)
{ {
pconf_.object_function = [this, bin] (
Pile& pile,
const Item &item,
double pile_area,
double norm,
double /*penality*/) {
auto result = objfunc(bin.center(), bin_area_, pile, pconf_.object_function = [this, bin] (const Item &item) {
pile_area, item, norm, areacache_, rtree_);
auto result = objfunc(bin.center(),
merged_pile_,
pilebb_,
items_,
item,
bin_area_,
norm_,
rtree_,
remaining_);
double score = std::get<0>(result); double score = std::get<0>(result);
auto& fullbb = std::get<1>(result); auto& fullbb = std::get<1>(result);
auto wdiff = fullbb.width() - bin.width(); double miss = Placer::overfit(fullbb, bin);
auto hdiff = fullbb.height() - bin.height(); miss = miss > 0? miss : 0;
if(wdiff > 0) score += std::pow(wdiff, 2) / norm; score += miss*miss;
if(hdiff > 0) score += std::pow(hdiff, 2) / norm;
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; return score;
}; };
@ -335,27 +424,20 @@ public:
std::function<void(unsigned)> progressind): std::function<void(unsigned)> progressind):
_ArrBase<PolygonImpl>(bin, dist, progressind) _ArrBase<PolygonImpl>(bin, dist, progressind)
{ {
pconf_.object_function = [this, &bin] ( pconf_.object_function = [this, &bin] (const Item &item) {
Pile& pile,
const Item &item,
double pile_area,
double norm,
double /*penality*/) {
auto binbb = ShapeLike::boundingBox(bin); auto binbb = sl::boundingBox(bin);
auto result = objfunc(binbb.center(), bin_area_, pile, auto result = objfunc(binbb.center(),
pile_area, item, norm, areacache_, rtree_); merged_pile_,
pilebb_,
items_,
item,
bin_area_,
norm_,
rtree_,
remaining_);
double score = std::get<0>(result); 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; return score;
}; };
@ -370,15 +452,17 @@ public:
AutoArranger(Distance dist, std::function<void(unsigned)> progressind): AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
_ArrBase<Box>(Box(0, 0), dist, progressind) _ArrBase<Box>(Box(0, 0), dist, progressind)
{ {
this->pconf_.object_function = [this] ( this->pconf_.object_function = [this] (const Item &item) {
Pile& pile,
const Item &item,
double pile_area,
double norm,
double /*penality*/) {
auto result = objfunc({0, 0}, 0, pile, pile_area, auto result = objfunc({0, 0},
item, norm, areacache_, rtree_); merged_pile_,
pilebb_,
items_,
item,
0,
norm_,
rtree_,
remaining_);
return std::get<0>(result); return std::get<0>(result);
}; };
@ -440,16 +524,113 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
return ret; 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, BOX,
CIRCLE, CIRCLE,
IRREGULAR, IRREGULAR,
WHO_KNOWS 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 // Determine the bed shape by hand
return BOX; return ret;
} }
void applyResult( void applyResult(
@ -468,8 +649,7 @@ void applyResult(
// appropriately // appropriately
auto off = item.translation(); auto off = item.translation();
Radians rot = item.rotation(); Radians rot = item.rotation();
Vec2d foff(off.X*SCALING_FACTOR + batch_offset, Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR);
off.Y*SCALING_FACTOR);
// write the tranformation data into the model instance // write the tranformation data into the model instance
inst_ptr->rotation = rot; inst_ptr->rotation = rot;
@ -525,7 +705,10 @@ bool arrange(Model &model, coordf_t min_obj_distance,
}); });
IndexedPackGroup result; IndexedPackGroup result;
BoundingBox bbb(bed.points);
if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed);
BoundingBox bbb(bed);
auto binbb = Box({ auto binbb = Box({
static_cast<libnest2d::Coord>(bbb.min(0)), 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)) static_cast<libnest2d::Coord>(bbb.max(1))
}); });
switch(bedhint) { switch(bedhint.type) {
case BOX: { case BedShapeType::BOX: {
// Create the arranger for the box shaped bed // Create the arranger for the box shaped bed
AutoArranger<Box> arrange(binbb, min_obj_distance, progressind); 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()); result = arrange(shapes.begin(), shapes.end());
break; 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; break;
case IRREGULAR: }
case WHO_KNOWS: { case BedShapeType::IRREGULAR:
case BedShapeType::WHO_KNOWS: {
using P = libnest2d::PolygonImpl; using P = libnest2d::PolygonImpl;
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
P irrbed = ShapeLike::create<PolygonImpl>(std::move(ctour)); P irrbed = sl::create<PolygonImpl>(std::move(ctour));
// std::cout << ShapeLike::toString(irrbed) << std::endl;
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind); 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) { if(first_bin_only) {
applyResult(result.front(), 0, shapemap); applyResult(result.front(), 0, shapemap);
} else { } else {

View file

@ -433,6 +433,7 @@ const PrintConfig &PrintController::config() const
return print_->config; return print_->config;
} }
void AppController::arrange_model() void AppController::arrange_model()
{ {
using Coord = libnest2d::TCoord<libnest2d::PointImpl>; using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
@ -468,21 +469,25 @@ void AppController::arrange_model()
if(pind) pind->update(0, _(L("Arranging objects..."))); if(pind) pind->update(0, _(L("Arranging objects...")));
try { try {
arr::BedShapeHint hint;
// TODO: from Sasha from GUI
hint.type = arr::BedShapeType::WHO_KNOWS;
arr::arrange(*model_, arr::arrange(*model_,
min_obj_distance, min_obj_distance,
bed, bed,
arr::BOX, hint,
false, // create many piles not just one pile false, // create many piles not just one pile
[this, pind, count](unsigned rem) { [pind, count](unsigned rem) {
if(pind) if(pind)
pind->update(count - rem, _(L("Arranging objects..."))); pind->update(count - rem, _(L("Arranging objects...")));
}); });
} catch(std::exception& e) { } catch(std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
report_issue(IssueType::ERR, report_issue(IssueType::ERR,
_(L("Could not arrange model objects! " _(L("Could not arrange model objects! "
"Some geometries may be invalid.")), "Some geometries may be invalid.")),
_(L("Exception occurred"))); _(L("Exception occurred")));
} }
// Restore previous max value // Restore previous max value