mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Merge remote-tracking branch 'origin/parallel_arrange'
This commit is contained in:
		
						commit
						7fc0b4375c
					
				
					 35 changed files with 3340 additions and 1991 deletions
				
			
		|  | @ -267,11 +267,10 @@ add_library(libslic3r_gui STATIC | |||
|     ${LIBDIR}/slic3r/Utils/Time.hpp | ||||
|     ${LIBDIR}/slic3r/Utils/HexFile.cpp | ||||
|     ${LIBDIR}/slic3r/Utils/HexFile.hpp | ||||
|     ${LIBDIR}/slic3r/IProgressIndicator.hpp | ||||
|     ${LIBDIR}/slic3r/ProgressIndicator.hpp | ||||
|     ${LIBDIR}/slic3r/AppController.hpp | ||||
|     ${LIBDIR}/slic3r/AppController.cpp | ||||
|     ${LIBDIR}/slic3r/AppControllerWx.cpp | ||||
|     ${LIBDIR}/slic3r/Strings.hpp | ||||
| ) | ||||
| 
 | ||||
| add_library(admesh STATIC | ||||
|  | @ -748,6 +747,7 @@ add_custom_target(pot | |||
| set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") | ||||
| 
 | ||||
| add_subdirectory(${LIBDIR}/libnest2d) | ||||
| target_compile_definitions(libslic3r PUBLIC -DUSE_TBB) | ||||
| target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES}) | ||||
| target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES}) | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ set(LIBNEST2D_SRCFILES | |||
|     ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/metaloop.hpp | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/rotfinder.hpp | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/placer_boilerplate.hpp | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp | ||||
|  | @ -89,14 +90,39 @@ if(LIBNEST2D_UNITTESTS) | |||
| endif() | ||||
| 
 | ||||
| if(LIBNEST2D_BUILD_EXAMPLES) | ||||
| 
 | ||||
|     add_executable(example examples/main.cpp | ||||
| #                           tools/libnfpglue.hpp | ||||
| #                           tools/libnfpglue.cpp | ||||
|                            tools/nfp_svgnest.hpp | ||||
|                            tools/nfp_svgnest_glue.hpp | ||||
|                            tools/svgtools.hpp | ||||
|                            tests/printer_parts.cpp | ||||
|                            tests/printer_parts.h | ||||
|                            ${LIBNEST2D_SRCFILES}) | ||||
|                            ${LIBNEST2D_SRCFILES} | ||||
|                            ) | ||||
|     set(TBB_STATIC ON) | ||||
|     find_package(TBB QUIET) | ||||
|     if(TBB_FOUND) | ||||
|         message(STATUS "Parallelization with Intel TBB") | ||||
|         target_include_directories(example PUBLIC ${TBB_INCLUDE_DIRS}) | ||||
|         target_compile_definitions(example PUBLIC ${TBB_DEFINITIONS} -DUSE_TBB) | ||||
|         if(MSVC) | ||||
|            # Suppress implicit linking of the TBB libraries by the Visual Studio compiler. | ||||
|            target_compile_definitions(example PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE) | ||||
|         endif() | ||||
|         # The Intel TBB library will use the std::exception_ptr feature of C++11. | ||||
|         target_compile_definitions(example PUBLIC -DTBB_USE_CAPTURED_EXCEPTION=1) | ||||
| 
 | ||||
|         target_link_libraries(example ${TBB_LIBRARIES}) | ||||
|    else() | ||||
|        find_package(OpenMP QUIET) | ||||
|        if(OpenMP_CXX_FOUND) | ||||
|            message(STATUS "Parallelization with OpenMP") | ||||
|            target_include_directories(example PUBLIC OpenMP::OpenMP_CXX) | ||||
|            target_link_libraries(example OpenMP::OpenMP_CXX) | ||||
|        endif() | ||||
|    endif() | ||||
| 
 | ||||
|     target_link_libraries(example ${LIBNEST2D_LIBRARIES}) | ||||
|     target_include_directories(example PUBLIC ${LIBNEST2D_HEADERS}) | ||||
|  |  | |||
|  | @ -9,18 +9,28 @@ with templated geometry types. These geometries can have custom or already | |||
| existing implementation to avoid copying or having unnecessary dependencies. | ||||
| 
 | ||||
| A default backend is provided if the user of the library just wants to use it  | ||||
| out of the box without additional integration. The default backend is reasonably  | ||||
| out of the box without additional integration. This backend is reasonably  | ||||
| fast and robust, being built on top of boost geometry and the  | ||||
| [polyclipping](http://www.angusj.com/delphi/clipper.php) library. Usage of  | ||||
| this default backend implies the dependency on these packages as well as the  | ||||
| compilation of the backend itself (The default backend is not yet header only). | ||||
| this default backend implies the dependency on these packages but its header  | ||||
| only as well. | ||||
| 
 | ||||
| This software is currently under construction and lacks a throughout  | ||||
| documentation and some essential algorithms as well. At this stage it works well | ||||
| for rectangles and convex closed polygons without considering holes and  | ||||
| concavities. | ||||
| 
 | ||||
| Holes and non-convex polygons will be usable in the near future as well. | ||||
| Holes and non-convex polygons will be usable in the near future as well. The  | ||||
| no fit polygon based placer module combined with the first fit selection  | ||||
| strategy is now used in the [Slic3r](https://github.com/prusa3d/Slic3r)  | ||||
| application's arrangement feature. It uses local optimization techniques to find | ||||
| the best placement of each new item based on some features of the arrangement. | ||||
| 
 | ||||
| In the near future I would like to use machine learning to evaluate the  | ||||
| placements and (or) the order if items in which they are placed and see what  | ||||
| results can be obtained. This is a different approach than that of SVGnest which  | ||||
| uses genetic algorithms to find better and better selection orders. Maybe the  | ||||
| two approaches can be combined as well. | ||||
| 
 | ||||
| # References | ||||
| - [SVGNest](https://github.com/Jack000/SVGnest) | ||||
|  |  | |||
							
								
								
									
										322
									
								
								xs/src/libnest2d/cmake_modules/FindTBB.cmake
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								xs/src/libnest2d/cmake_modules/FindTBB.cmake
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,322 @@ | |||
| # The MIT License (MIT) | ||||
| # | ||||
| # Copyright (c) 2015 Justus Calvin | ||||
| #  | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to deal | ||||
| # in the Software without restriction, including without limitation the rights | ||||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| # copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| #  | ||||
| # The above copyright notice and this permission notice shall be included in all | ||||
| # copies or substantial portions of the Software. | ||||
| #  | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| # SOFTWARE. | ||||
| 
 | ||||
| # | ||||
| # FindTBB | ||||
| # ------- | ||||
| # | ||||
| # Find TBB include directories and libraries. | ||||
| # | ||||
| # Usage: | ||||
| # | ||||
| #  find_package(TBB [major[.minor]] [EXACT] | ||||
| #               [QUIET] [REQUIRED] | ||||
| #               [[COMPONENTS] [components...]] | ||||
| #               [OPTIONAL_COMPONENTS components...])  | ||||
| # | ||||
| # where the allowed components are tbbmalloc and tbb_preview. Users may modify  | ||||
| # the behavior of this module with the following variables: | ||||
| # | ||||
| # * TBB_ROOT_DIR          - The base directory the of TBB installation. | ||||
| # * TBB_INCLUDE_DIR       - The directory that contains the TBB headers files. | ||||
| # * TBB_LIBRARY           - The directory that contains the TBB library files. | ||||
| # * TBB_<library>_LIBRARY - The path of the TBB the corresponding TBB library.  | ||||
| #                           These libraries, if specified, override the  | ||||
| #                           corresponding library search results, where <library> | ||||
| #                           may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug, | ||||
| #                           tbb_preview, or tbb_preview_debug. | ||||
| # * TBB_USE_DEBUG_BUILD   - The debug version of tbb libraries, if present, will | ||||
| #                           be used instead of the release version. | ||||
| # * TBB_STATIC            - Static linking of libraries with a _static suffix. | ||||
| #                           For example, on Windows a tbb_static.lib will be searched for | ||||
| #                           instead of tbb.lib. | ||||
| # | ||||
| # Users may modify the behavior of this module with the following environment | ||||
| # variables: | ||||
| # | ||||
| # * TBB_INSTALL_DIR  | ||||
| # * TBBROOT | ||||
| # * LIBRARY_PATH | ||||
| # | ||||
| # This module will set the following variables: | ||||
| # | ||||
| # * TBB_FOUND             - Set to false, or undefined, if we haven’t found, or | ||||
| #                           don’t want to use TBB. | ||||
| # * TBB_<component>_FOUND - If False, optional <component> part of TBB sytem is | ||||
| #                           not available. | ||||
| # * TBB_VERSION           - The full version string | ||||
| # * TBB_VERSION_MAJOR     - The major version | ||||
| # * TBB_VERSION_MINOR     - The minor version | ||||
| # * TBB_INTERFACE_VERSION - The interface version number defined in  | ||||
| #                           tbb/tbb_stddef.h. | ||||
| # * TBB_<library>_LIBRARY_RELEASE - The path of the TBB release version of  | ||||
| #                           <library>, where <library> may be tbb, tbb_debug, | ||||
| #                           tbbmalloc, tbbmalloc_debug, tbb_preview, or  | ||||
| #                           tbb_preview_debug. | ||||
| # * TBB_<library>_LIBRARY_DEGUG - The path of the TBB release version of  | ||||
| #                           <library>, where <library> may be tbb, tbb_debug, | ||||
| #                           tbbmalloc, tbbmalloc_debug, tbb_preview, or  | ||||
| #                           tbb_preview_debug. | ||||
| # | ||||
| # The following varibles should be used to build and link with TBB: | ||||
| # | ||||
| # * TBB_INCLUDE_DIRS        - The include directory for TBB. | ||||
| # * TBB_LIBRARIES           - The libraries to link against to use TBB. | ||||
| # * TBB_LIBRARIES_RELEASE   - The release libraries to link against to use TBB. | ||||
| # * TBB_LIBRARIES_DEBUG     - The debug libraries to link against to use TBB. | ||||
| # * TBB_DEFINITIONS         - Definitions to use when compiling code that uses | ||||
| #                             TBB. | ||||
| # * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that | ||||
| #                             uses TBB. | ||||
| # * TBB_DEFINITIONS_DEBUG   - Definitions to use when compiling debug code that | ||||
| #                             uses TBB. | ||||
| # | ||||
| # This module will also create the "tbb" target that may be used when building | ||||
| # executables and libraries. | ||||
| 
 | ||||
| include(FindPackageHandleStandardArgs) | ||||
| 
 | ||||
| if(NOT TBB_FOUND) | ||||
| 
 | ||||
|   ################################## | ||||
|   # Check the build type | ||||
|   ################################## | ||||
|    | ||||
|   if(NOT DEFINED TBB_USE_DEBUG_BUILD) | ||||
|     if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)") | ||||
|       set(TBB_BUILD_TYPE DEBUG) | ||||
|     else() | ||||
|       set(TBB_BUILD_TYPE RELEASE) | ||||
|     endif() | ||||
|   elseif(TBB_USE_DEBUG_BUILD) | ||||
|     set(TBB_BUILD_TYPE DEBUG) | ||||
|   else() | ||||
|     set(TBB_BUILD_TYPE RELEASE) | ||||
|   endif() | ||||
|    | ||||
|   ################################## | ||||
|   # Set the TBB search directories | ||||
|   ################################## | ||||
|    | ||||
|   # Define search paths based on user input and environment variables | ||||
|   set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT}) | ||||
|    | ||||
|   # Define the search directories based on the current platform | ||||
|   if(CMAKE_SYSTEM_NAME STREQUAL "Windows") | ||||
|     set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB" | ||||
|                                "C:/Program Files (x86)/Intel/TBB") | ||||
| 
 | ||||
|     # Set the target architecture | ||||
|     if(CMAKE_SIZEOF_VOID_P EQUAL 8) | ||||
|       set(TBB_ARCHITECTURE "intel64") | ||||
|     else() | ||||
|       set(TBB_ARCHITECTURE "ia32") | ||||
|     endif() | ||||
| 
 | ||||
|     # Set the TBB search library path search suffix based on the version of VC | ||||
|     if(WINDOWS_STORE) | ||||
|       set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui") | ||||
|     elseif(MSVC14) | ||||
|       set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14") | ||||
|     elseif(MSVC12) | ||||
|       set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12") | ||||
|     elseif(MSVC11) | ||||
|       set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11") | ||||
|     elseif(MSVC10) | ||||
|       set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10") | ||||
|     endif() | ||||
| 
 | ||||
|     # Add the library path search suffix for the VC independent version of TBB | ||||
|     list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt") | ||||
| 
 | ||||
|   elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") | ||||
|     # OS X | ||||
|     set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") | ||||
|      | ||||
|     # TODO: Check to see which C++ library is being used by the compiler. | ||||
|     if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0) | ||||
|       # The default C++ library on OS X 10.9 and later is libc++ | ||||
|       set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib") | ||||
|     else() | ||||
|       set(TBB_LIB_PATH_SUFFIX "lib") | ||||
|     endif() | ||||
|   elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") | ||||
|     # Linux | ||||
|     set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") | ||||
|      | ||||
|     # TODO: Check compiler version to see the suffix should be <arch>/gcc4.1 or | ||||
|     #       <arch>/gcc4.1. For now, assume that the compiler is more recent than | ||||
|     #       gcc 4.4.x or later. | ||||
|     if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") | ||||
|       set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4") | ||||
|     elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") | ||||
|       set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4") | ||||
|     endif() | ||||
|   endif() | ||||
|    | ||||
|   ################################## | ||||
|   # Find the TBB include dir | ||||
|   ################################## | ||||
|    | ||||
|   find_path(TBB_INCLUDE_DIRS tbb/tbb.h | ||||
|       HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR} | ||||
|       PATHS ${TBB_DEFAULT_SEARCH_DIR} | ||||
|       PATH_SUFFIXES include) | ||||
| 
 | ||||
|   ################################## | ||||
|   # Set version strings | ||||
|   ################################## | ||||
| 
 | ||||
|   if(TBB_INCLUDE_DIRS) | ||||
|     file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file) | ||||
|     string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" | ||||
|         TBB_VERSION_MAJOR "${_tbb_version_file}") | ||||
|     string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" | ||||
|         TBB_VERSION_MINOR "${_tbb_version_file}") | ||||
|     string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" | ||||
|         TBB_INTERFACE_VERSION "${_tbb_version_file}") | ||||
|     set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}") | ||||
|   endif() | ||||
| 
 | ||||
|   ################################## | ||||
|   # Find TBB components | ||||
|   ################################## | ||||
| 
 | ||||
|   if(TBB_VERSION VERSION_LESS 4.3) | ||||
|     set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb) | ||||
|   else() | ||||
|     set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb) | ||||
|   endif() | ||||
| 
 | ||||
|   if(TBB_STATIC) | ||||
|     set(TBB_STATIC_SUFFIX "_static") | ||||
|   endif() | ||||
| 
 | ||||
|   # Find each component | ||||
|   foreach(_comp ${TBB_SEARCH_COMPOMPONENTS}) | ||||
|     if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") | ||||
| 
 | ||||
|       # Search for the libraries | ||||
|       find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX} | ||||
|           HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} | ||||
|           PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH | ||||
|           PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) | ||||
| 
 | ||||
|       find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug | ||||
|           HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} | ||||
|           PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH | ||||
|           PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) | ||||
| 
 | ||||
|       if(TBB_${_comp}_LIBRARY_DEBUG) | ||||
|         list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}") | ||||
|       endif() | ||||
|       if(TBB_${_comp}_LIBRARY_RELEASE) | ||||
|         list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}") | ||||
|       endif() | ||||
|       if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY) | ||||
|         set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}") | ||||
|       endif() | ||||
| 
 | ||||
|       if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}") | ||||
|         set(TBB_${_comp}_FOUND TRUE) | ||||
|       else() | ||||
|         set(TBB_${_comp}_FOUND FALSE) | ||||
|       endif() | ||||
| 
 | ||||
|       # Mark internal variables as advanced | ||||
|       mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) | ||||
|       mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) | ||||
|       mark_as_advanced(TBB_${_comp}_LIBRARY) | ||||
| 
 | ||||
|     endif() | ||||
|   endforeach() | ||||
| 
 | ||||
|   unset(TBB_STATIC_SUFFIX) | ||||
| 
 | ||||
|   ################################## | ||||
|   # Set compile flags and libraries | ||||
|   ################################## | ||||
| 
 | ||||
|   set(TBB_DEFINITIONS_RELEASE "") | ||||
|   set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1") | ||||
|      | ||||
|   if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) | ||||
|     set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}") | ||||
|     set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") | ||||
|   elseif(TBB_LIBRARIES_RELEASE) | ||||
|     set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}") | ||||
|     set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}") | ||||
|   elseif(TBB_LIBRARIES_DEBUG) | ||||
|     set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}") | ||||
|     set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}") | ||||
|   endif() | ||||
| 
 | ||||
|   find_package_handle_standard_args(TBB  | ||||
|       REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES | ||||
|       HANDLE_COMPONENTS | ||||
|       VERSION_VAR TBB_VERSION) | ||||
| 
 | ||||
|   ################################## | ||||
|   # Create targets | ||||
|   ################################## | ||||
| 
 | ||||
|   if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) | ||||
|     add_library(tbb SHARED IMPORTED) | ||||
|     set_target_properties(tbb PROPERTIES | ||||
|           INTERFACE_INCLUDE_DIRECTORIES  ${TBB_INCLUDE_DIRS} | ||||
|           IMPORTED_LOCATION              ${TBB_LIBRARIES}) | ||||
|     if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG) | ||||
|       set_target_properties(tbb PROPERTIES | ||||
|           INTERFACE_COMPILE_DEFINITIONS "$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:TBB_USE_DEBUG=1>" | ||||
|           IMPORTED_LOCATION_DEBUG          ${TBB_LIBRARIES_DEBUG} | ||||
|           IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG} | ||||
|           IMPORTED_LOCATION_RELEASE        ${TBB_LIBRARIES_RELEASE} | ||||
|           IMPORTED_LOCATION_MINSIZEREL     ${TBB_LIBRARIES_RELEASE} | ||||
|           ) | ||||
|     elseif(TBB_LIBRARIES_RELEASE) | ||||
|       set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE}) | ||||
|     else() | ||||
|       set_target_properties(tbb PROPERTIES | ||||
|           INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}" | ||||
|           IMPORTED_LOCATION              ${TBB_LIBRARIES_DEBUG} | ||||
|           ) | ||||
|     endif() | ||||
|   endif() | ||||
| 
 | ||||
|   mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES) | ||||
| 
 | ||||
|   unset(TBB_ARCHITECTURE) | ||||
|   unset(TBB_BUILD_TYPE) | ||||
|   unset(TBB_LIB_PATH_SUFFIX) | ||||
|   unset(TBB_DEFAULT_SEARCH_DIR) | ||||
| 
 | ||||
|   if(TBB_DEBUG) | ||||
|     message(STATUS "  TBB_INCLUDE_DIRS        = ${TBB_INCLUDE_DIRS}") | ||||
|     message(STATUS "  TBB_DEFINITIONS         = ${TBB_DEFINITIONS}") | ||||
|     message(STATUS "  TBB_LIBRARIES           = ${TBB_LIBRARIES}") | ||||
|     message(STATUS "  TBB_DEFINITIONS_DEBUG   = ${TBB_DEFINITIONS_DEBUG}") | ||||
|     message(STATUS "  TBB_LIBRARIES_DEBUG     = ${TBB_LIBRARIES_DEBUG}") | ||||
|     message(STATUS "  TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}") | ||||
|     message(STATUS "  TBB_LIBRARIES_RELEASE   = ${TBB_LIBRARIES_RELEASE}") | ||||
|   endif() | ||||
| 
 | ||||
| endif() | ||||
|  | @ -1,7 +1,6 @@ | |||
| #include <iostream> | ||||
| #include <string> | ||||
| #include <fstream> | ||||
| 
 | ||||
| //#define DEBUG_EXPORT_NFP
 | ||||
| 
 | ||||
| #include <libnest2d.h> | ||||
|  | @ -9,7 +8,11 @@ | |||
| #include "tests/printer_parts.h" | ||||
| #include "tools/benchmark.h" | ||||
| #include "tools/svgtools.hpp" | ||||
| #include "libnest2d/rotfinder.hpp" | ||||
| 
 | ||||
| //#include "tools/libnfpglue.hpp"
 | ||||
| //#include "tools/nfp_svgnest_glue.hpp"
 | ||||
| 
 | ||||
| 
 | ||||
| using namespace libnest2d; | ||||
| using ItemGroup = std::vector<std::reference_wrapper<Item>>; | ||||
|  | @ -50,499 +53,57 @@ void arrangeRectangles() { | |||
|     using namespace libnest2d; | ||||
| 
 | ||||
|     const int SCALE = 1000000; | ||||
| //    const int SCALE = 1;
 | ||||
|     std::vector<Rectangle> rects = { | ||||
|         {80*SCALE, 80*SCALE}, | ||||
|         {60*SCALE, 90*SCALE}, | ||||
|         {70*SCALE, 30*SCALE}, | ||||
|         {80*SCALE, 60*SCALE}, | ||||
|         {60*SCALE, 60*SCALE}, | ||||
|         {60*SCALE, 40*SCALE}, | ||||
|         {40*SCALE, 40*SCALE}, | ||||
|         {10*SCALE, 10*SCALE}, | ||||
|         {10*SCALE, 10*SCALE}, | ||||
|         {10*SCALE, 10*SCALE}, | ||||
|         {10*SCALE, 10*SCALE}, | ||||
|         {10*SCALE, 10*SCALE}, | ||||
|         {5*SCALE, 5*SCALE}, | ||||
|         {5*SCALE, 5*SCALE}, | ||||
|         {5*SCALE, 5*SCALE}, | ||||
|         {5*SCALE, 5*SCALE}, | ||||
|         {5*SCALE, 5*SCALE}, | ||||
|         {5*SCALE, 5*SCALE}, | ||||
|         {5*SCALE, 5*SCALE}, | ||||
|         {20*SCALE, 20*SCALE} | ||||
|        }; | ||||
| 
 | ||||
| //    std::vector<Rectangle> rects = {
 | ||||
| //        {20*SCALE, 10*SCALE},
 | ||||
| //        {20*SCALE, 10*SCALE},
 | ||||
| //        {20*SCALE, 20*SCALE},
 | ||||
| //    };
 | ||||
|     std::vector<Item> rects(202,       { | ||||
|                                      {-9945219, -3065619}, | ||||
|                                      {-9781479, -2031780}, | ||||
|                                      {-9510560, -1020730}, | ||||
|                                      {-9135450, -43529}, | ||||
|                                      {-2099999, 14110899}, | ||||
|                                      {2099999, 14110899}, | ||||
|                                      {9135450, -43529}, | ||||
|                                      {9510560, -1020730}, | ||||
|                                      {9781479, -2031780}, | ||||
|                                      {9945219, -3065619}, | ||||
|                                      {10000000, -4110899}, | ||||
|                                      {9945219, -5156179}, | ||||
|                                      {9781479, -6190019}, | ||||
|                                      {9510560, -7201069}, | ||||
|                                      {9135450, -8178270}, | ||||
|                                      {8660249, -9110899}, | ||||
|                                      {8090169, -9988750}, | ||||
|                                      {7431449, -10802209}, | ||||
|                                      {6691309, -11542349}, | ||||
|                                      {5877850, -12201069}, | ||||
|                                      {5000000, -12771149}, | ||||
|                                      {4067369, -13246350}, | ||||
|                                      {3090169, -13621459}, | ||||
|                                      {2079119, -13892379}, | ||||
|                                      {1045279, -14056119}, | ||||
|                                      {0, -14110899}, | ||||
|                                      {-1045279, -14056119}, | ||||
|                                      {-2079119, -13892379}, | ||||
|                                      {-3090169, -13621459}, | ||||
|                                      {-4067369, -13246350}, | ||||
|                                      {-5000000, -12771149}, | ||||
|                                      {-5877850, -12201069}, | ||||
|                                      {-6691309, -11542349}, | ||||
|                                      {-7431449, -10802209}, | ||||
|                                      {-8090169, -9988750}, | ||||
|                                      {-8660249, -9110899}, | ||||
|                                      {-9135450, -8178270}, | ||||
|                                      {-9510560, -7201069}, | ||||
|                                      {-9781479, -6190019}, | ||||
|                                      {-9945219, -5156179}, | ||||
|                                      {-10000000, -4110899}, | ||||
|                                      {-9945219, -3065619}, | ||||
|                                  }); | ||||
| 
 | ||||
| //    std::vector<Item> input {
 | ||||
| //        {{0, 0}, {0, 20*SCALE}, {10*SCALE, 0}, {0, 0}}
 | ||||
| //    };
 | ||||
| 
 | ||||
|     std::vector<Item> crasher = | ||||
|     { | ||||
|         { | ||||
|             {-5000000, 8954050}, | ||||
|             {5000000, 8954050}, | ||||
|             {5000000, -45949}, | ||||
|             {4972609, -568549}, | ||||
|             {3500000, -8954050}, | ||||
|             {-3500000, -8954050}, | ||||
|             {-4972609, -568549}, | ||||
|             {-5000000, -45949}, | ||||
|             {-5000000, 8954050}, | ||||
|         }, | ||||
|         { | ||||
|             {-5000000, 8954050}, | ||||
|             {5000000, 8954050}, | ||||
|             {5000000, -45949}, | ||||
|             {4972609, -568549}, | ||||
|             {3500000, -8954050}, | ||||
|             {-3500000, -8954050}, | ||||
|             {-4972609, -568549}, | ||||
|             {-5000000, -45949}, | ||||
|             {-5000000, 8954050}, | ||||
|         }, | ||||
|         { | ||||
|             {-5000000, 8954050}, | ||||
|             {5000000, 8954050}, | ||||
|             {5000000, -45949}, | ||||
|             {4972609, -568549}, | ||||
|             {3500000, -8954050}, | ||||
|             {-3500000, -8954050}, | ||||
|             {-4972609, -568549}, | ||||
|             {-5000000, -45949}, | ||||
|             {-5000000, 8954050}, | ||||
|         }, | ||||
|         { | ||||
|             {-5000000, 8954050}, | ||||
|             {5000000, 8954050}, | ||||
|             {5000000, -45949}, | ||||
|             {4972609, -568549}, | ||||
|             {3500000, -8954050}, | ||||
|             {-3500000, -8954050}, | ||||
|             {-4972609, -568549}, | ||||
|             {-5000000, -45949}, | ||||
|             {-5000000, 8954050}, | ||||
|         }, | ||||
|         { | ||||
|             {-5000000, 8954050}, | ||||
|             {5000000, 8954050}, | ||||
|             {5000000, -45949}, | ||||
|             {4972609, -568549}, | ||||
|             {3500000, -8954050}, | ||||
|             {-3500000, -8954050}, | ||||
|             {-4972609, -568549}, | ||||
|             {-5000000, -45949}, | ||||
|             {-5000000, 8954050}, | ||||
|         }, | ||||
|         { | ||||
|             {-5000000, 8954050}, | ||||
|             {5000000, 8954050}, | ||||
|             {5000000, -45949}, | ||||
|             {4972609, -568549}, | ||||
|             {3500000, -8954050}, | ||||
|             {-3500000, -8954050}, | ||||
|             {-4972609, -568549}, | ||||
|             {-5000000, -45949}, | ||||
|             {-5000000, 8954050}, | ||||
|         }, | ||||
|         { | ||||
|             {-9945219, -3065619}, | ||||
|             {-9781479, -2031780}, | ||||
|             {-9510560, -1020730}, | ||||
|             {-9135450, -43529}, | ||||
|             {-2099999, 14110899}, | ||||
|             {2099999, 14110899}, | ||||
|             {9135450, -43529}, | ||||
|             {9510560, -1020730}, | ||||
|             {9781479, -2031780}, | ||||
|             {9945219, -3065619}, | ||||
|             {10000000, -4110899}, | ||||
|             {9945219, -5156179}, | ||||
|             {9781479, -6190020}, | ||||
|             {9510560, -7201069}, | ||||
|             {9135450, -8178270}, | ||||
|             {8660249, -9110899}, | ||||
|             {8090169, -9988750}, | ||||
|             {7431449, -10802200}, | ||||
|             {6691309, -11542300}, | ||||
|             {5877850, -12201100}, | ||||
|             {5000000, -12771100}, | ||||
|             {4067369, -13246399}, | ||||
|             {3090169, -13621500}, | ||||
|             {2079119, -13892399}, | ||||
|             {1045279, -14056099}, | ||||
|             {0, -14110899}, | ||||
|             {-1045279, -14056099}, | ||||
|             {-2079119, -13892399}, | ||||
|             {-3090169, -13621500}, | ||||
|             {-4067369, -13246399}, | ||||
|             {-5000000, -12771100}, | ||||
|             {-5877850, -12201100}, | ||||
|             {-6691309, -11542300}, | ||||
|             {-7431449, -10802200}, | ||||
|             {-8090169, -9988750}, | ||||
|             {-8660249, -9110899}, | ||||
|             {-9135450, -8178270}, | ||||
|             {-9510560, -7201069}, | ||||
|             {-9781479, -6190020}, | ||||
|             {-9945219, -5156179}, | ||||
|             {-10000000, -4110899}, | ||||
|             {-9945219, -3065619}, | ||||
|         }, | ||||
|         { | ||||
|             {-9945219, -3065619}, | ||||
|             {-9781479, -2031780}, | ||||
|             {-9510560, -1020730}, | ||||
|             {-9135450, -43529}, | ||||
|             {-2099999, 14110899}, | ||||
|             {2099999, 14110899}, | ||||
|             {9135450, -43529}, | ||||
|             {9510560, -1020730}, | ||||
|             {9781479, -2031780}, | ||||
|             {9945219, -3065619}, | ||||
|             {10000000, -4110899}, | ||||
|             {9945219, -5156179}, | ||||
|             {9781479, -6190020}, | ||||
|             {9510560, -7201069}, | ||||
|             {9135450, -8178270}, | ||||
|             {8660249, -9110899}, | ||||
|             {8090169, -9988750}, | ||||
|             {7431449, -10802200}, | ||||
|             {6691309, -11542300}, | ||||
|             {5877850, -12201100}, | ||||
|             {5000000, -12771100}, | ||||
|             {4067369, -13246399}, | ||||
|             {3090169, -13621500}, | ||||
|             {2079119, -13892399}, | ||||
|             {1045279, -14056099}, | ||||
|             {0, -14110899}, | ||||
|             {-1045279, -14056099}, | ||||
|             {-2079119, -13892399}, | ||||
|             {-3090169, -13621500}, | ||||
|             {-4067369, -13246399}, | ||||
|             {-5000000, -12771100}, | ||||
|             {-5877850, -12201100}, | ||||
|             {-6691309, -11542300}, | ||||
|             {-7431449, -10802200}, | ||||
|             {-8090169, -9988750}, | ||||
|             {-8660249, -9110899}, | ||||
|             {-9135450, -8178270}, | ||||
|             {-9510560, -7201069}, | ||||
|             {-9781479, -6190020}, | ||||
|             {-9945219, -5156179}, | ||||
|             {-10000000, -4110899}, | ||||
|             {-9945219, -3065619}, | ||||
|         }, | ||||
|         { | ||||
|             {-9945219, -3065619}, | ||||
|             {-9781479, -2031780}, | ||||
|             {-9510560, -1020730}, | ||||
|             {-9135450, -43529}, | ||||
|             {-2099999, 14110899}, | ||||
|             {2099999, 14110899}, | ||||
|             {9135450, -43529}, | ||||
|             {9510560, -1020730}, | ||||
|             {9781479, -2031780}, | ||||
|             {9945219, -3065619}, | ||||
|             {10000000, -4110899}, | ||||
|             {9945219, -5156179}, | ||||
|             {9781479, -6190020}, | ||||
|             {9510560, -7201069}, | ||||
|             {9135450, -8178270}, | ||||
|             {8660249, -9110899}, | ||||
|             {8090169, -9988750}, | ||||
|             {7431449, -10802200}, | ||||
|             {6691309, -11542300}, | ||||
|             {5877850, -12201100}, | ||||
|             {5000000, -12771100}, | ||||
|             {4067369, -13246399}, | ||||
|             {3090169, -13621500}, | ||||
|             {2079119, -13892399}, | ||||
|             {1045279, -14056099}, | ||||
|             {0, -14110899}, | ||||
|             {-1045279, -14056099}, | ||||
|             {-2079119, -13892399}, | ||||
|             {-3090169, -13621500}, | ||||
|             {-4067369, -13246399}, | ||||
|             {-5000000, -12771100}, | ||||
|             {-5877850, -12201100}, | ||||
|             {-6691309, -11542300}, | ||||
|             {-7431449, -10802200}, | ||||
|             {-8090169, -9988750}, | ||||
|             {-8660249, -9110899}, | ||||
|             {-9135450, -8178270}, | ||||
|             {-9510560, -7201069}, | ||||
|             {-9781479, -6190020}, | ||||
|             {-9945219, -5156179}, | ||||
|             {-10000000, -4110899}, | ||||
|             {-9945219, -3065619}, | ||||
|         }, | ||||
|         { | ||||
|             {-9945219, -3065619}, | ||||
|             {-9781479, -2031780}, | ||||
|             {-9510560, -1020730}, | ||||
|             {-9135450, -43529}, | ||||
|             {-2099999, 14110899}, | ||||
|             {2099999, 14110899}, | ||||
|             {9135450, -43529}, | ||||
|             {9510560, -1020730}, | ||||
|             {9781479, -2031780}, | ||||
|             {9945219, -3065619}, | ||||
|             {10000000, -4110899}, | ||||
|             {9945219, -5156179}, | ||||
|             {9781479, -6190020}, | ||||
|             {9510560, -7201069}, | ||||
|             {9135450, -8178270}, | ||||
|             {8660249, -9110899}, | ||||
|             {8090169, -9988750}, | ||||
|             {7431449, -10802200}, | ||||
|             {6691309, -11542300}, | ||||
|             {5877850, -12201100}, | ||||
|             {5000000, -12771100}, | ||||
|             {4067369, -13246399}, | ||||
|             {3090169, -13621500}, | ||||
|             {2079119, -13892399}, | ||||
|             {1045279, -14056099}, | ||||
|             {0, -14110899}, | ||||
|             {-1045279, -14056099}, | ||||
|             {-2079119, -13892399}, | ||||
|             {-3090169, -13621500}, | ||||
|             {-4067369, -13246399}, | ||||
|             {-5000000, -12771100}, | ||||
|             {-5877850, -12201100}, | ||||
|             {-6691309, -11542300}, | ||||
|             {-7431449, -10802200}, | ||||
|             {-8090169, -9988750}, | ||||
|             {-8660249, -9110899}, | ||||
|             {-9135450, -8178270}, | ||||
|             {-9510560, -7201069}, | ||||
|             {-9781479, -6190020}, | ||||
|             {-9945219, -5156179}, | ||||
|             {-10000000, -4110899}, | ||||
|             {-9945219, -3065619}, | ||||
|         }, | ||||
|         { | ||||
|             {-9945219, -3065619}, | ||||
|             {-9781479, -2031780}, | ||||
|             {-9510560, -1020730}, | ||||
|             {-9135450, -43529}, | ||||
|             {-2099999, 14110899}, | ||||
|             {2099999, 14110899}, | ||||
|             {9135450, -43529}, | ||||
|             {9510560, -1020730}, | ||||
|             {9781479, -2031780}, | ||||
|             {9945219, -3065619}, | ||||
|             {10000000, -4110899}, | ||||
|             {9945219, -5156179}, | ||||
|             {9781479, -6190020}, | ||||
|             {9510560, -7201069}, | ||||
|             {9135450, -8178270}, | ||||
|             {8660249, -9110899}, | ||||
|             {8090169, -9988750}, | ||||
|             {7431449, -10802200}, | ||||
|             {6691309, -11542300}, | ||||
|             {5877850, -12201100}, | ||||
|             {5000000, -12771100}, | ||||
|             {4067369, -13246399}, | ||||
|             {3090169, -13621500}, | ||||
|             {2079119, -13892399}, | ||||
|             {1045279, -14056099}, | ||||
|             {0, -14110899}, | ||||
|             {-1045279, -14056099}, | ||||
|             {-2079119, -13892399}, | ||||
|             {-3090169, -13621500}, | ||||
|             {-4067369, -13246399}, | ||||
|             {-5000000, -12771100}, | ||||
|             {-5877850, -12201100}, | ||||
|             {-6691309, -11542300}, | ||||
|             {-7431449, -10802200}, | ||||
|             {-8090169, -9988750}, | ||||
|             {-8660249, -9110899}, | ||||
|             {-9135450, -8178270}, | ||||
|             {-9510560, -7201069}, | ||||
|             {-9781479, -6190020}, | ||||
|             {-9945219, -5156179}, | ||||
|             {-10000000, -4110899}, | ||||
|             {-9945219, -3065619}, | ||||
|         }, | ||||
|         { | ||||
|             {-9945219, -3065619}, | ||||
|             {-9781479, -2031780}, | ||||
|             {-9510560, -1020730}, | ||||
|             {-9135450, -43529}, | ||||
|             {-2099999, 14110899}, | ||||
|             {2099999, 14110899}, | ||||
|             {9135450, -43529}, | ||||
|             {9510560, -1020730}, | ||||
|             {9781479, -2031780}, | ||||
|             {9945219, -3065619}, | ||||
|             {10000000, -4110899}, | ||||
|             {9945219, -5156179}, | ||||
|             {9781479, -6190020}, | ||||
|             {9510560, -7201069}, | ||||
|             {9135450, -8178270}, | ||||
|             {8660249, -9110899}, | ||||
|             {8090169, -9988750}, | ||||
|             {7431449, -10802200}, | ||||
|             {6691309, -11542300}, | ||||
|             {5877850, -12201100}, | ||||
|             {5000000, -12771100}, | ||||
|             {4067369, -13246399}, | ||||
|             {3090169, -13621500}, | ||||
|             {2079119, -13892399}, | ||||
|             {1045279, -14056099}, | ||||
|             {0, -14110899}, | ||||
|             {-1045279, -14056099}, | ||||
|             {-2079119, -13892399}, | ||||
|             {-3090169, -13621500}, | ||||
|             {-4067369, -13246399}, | ||||
|             {-5000000, -12771100}, | ||||
|             {-5877850, -12201100}, | ||||
|             {-6691309, -11542300}, | ||||
|             {-7431449, -10802200}, | ||||
|             {-8090169, -9988750}, | ||||
|             {-8660249, -9110899}, | ||||
|             {-9135450, -8178270}, | ||||
|             {-9510560, -7201069}, | ||||
|             {-9781479, -6190020}, | ||||
|             {-9945219, -5156179}, | ||||
|             {-10000000, -4110899}, | ||||
|             {-9945219, -3065619}, | ||||
|         }, | ||||
|         { | ||||
|             {-9945219, -3065619}, | ||||
|             {-9781479, -2031780}, | ||||
|             {-9510560, -1020730}, | ||||
|             {-9135450, -43529}, | ||||
|             {-2099999, 14110899}, | ||||
|             {2099999, 14110899}, | ||||
|             {9135450, -43529}, | ||||
|             {9510560, -1020730}, | ||||
|             {9781479, -2031780}, | ||||
|             {9945219, -3065619}, | ||||
|             {10000000, -4110899}, | ||||
|             {9945219, -5156179}, | ||||
|             {9781479, -6190020}, | ||||
|             {9510560, -7201069}, | ||||
|             {9135450, -8178270}, | ||||
|             {8660249, -9110899}, | ||||
|             {8090169, -9988750}, | ||||
|             {7431449, -10802200}, | ||||
|             {6691309, -11542300}, | ||||
|             {5877850, -12201100}, | ||||
|             {5000000, -12771100}, | ||||
|             {4067369, -13246399}, | ||||
|             {3090169, -13621500}, | ||||
|             {2079119, -13892399}, | ||||
|             {1045279, -14056099}, | ||||
|             {0, -14110899}, | ||||
|             {-1045279, -14056099}, | ||||
|             {-2079119, -13892399}, | ||||
|             {-3090169, -13621500}, | ||||
|             {-4067369, -13246399}, | ||||
|             {-5000000, -12771100}, | ||||
|             {-5877850, -12201100}, | ||||
|             {-6691309, -11542300}, | ||||
|             {-7431449, -10802200}, | ||||
|             {-8090169, -9988750}, | ||||
|             {-8660249, -9110899}, | ||||
|             {-9135450, -8178270}, | ||||
|             {-9510560, -7201069}, | ||||
|             {-9781479, -6190020}, | ||||
|             {-9945219, -5156179}, | ||||
|             {-10000000, -4110899}, | ||||
|             {-9945219, -3065619}, | ||||
|         }, | ||||
|         { | ||||
|             {-9945219, -3065619}, | ||||
|             {-9781479, -2031780}, | ||||
|             {-9510560, -1020730}, | ||||
|             {-9135450, -43529}, | ||||
|             {-2099999, 14110899}, | ||||
|             {2099999, 14110899}, | ||||
|             {9135450, -43529}, | ||||
|             {9510560, -1020730}, | ||||
|             {9781479, -2031780}, | ||||
|             {9945219, -3065619}, | ||||
|             {10000000, -4110899}, | ||||
|             {9945219, -5156179}, | ||||
|             {9781479, -6190020}, | ||||
|             {9510560, -7201069}, | ||||
|             {9135450, -8178270}, | ||||
|             {8660249, -9110899}, | ||||
|             {8090169, -9988750}, | ||||
|             {7431449, -10802200}, | ||||
|             {6691309, -11542300}, | ||||
|             {5877850, -12201100}, | ||||
|             {5000000, -12771100}, | ||||
|             {4067369, -13246399}, | ||||
|             {3090169, -13621500}, | ||||
|             {2079119, -13892399}, | ||||
|             {1045279, -14056099}, | ||||
|             {0, -14110899}, | ||||
|             {-1045279, -14056099}, | ||||
|             {-2079119, -13892399}, | ||||
|             {-3090169, -13621500}, | ||||
|             {-4067369, -13246399}, | ||||
|             {-5000000, -12771100}, | ||||
|             {-5877850, -12201100}, | ||||
|             {-6691309, -11542300}, | ||||
|             {-7431449, -10802200}, | ||||
|             {-8090169, -9988750}, | ||||
|             {-8660249, -9110899}, | ||||
|             {-9135450, -8178270}, | ||||
|             {-9510560, -7201069}, | ||||
|             {-9781479, -6190020}, | ||||
|             {-9945219, -5156179}, | ||||
|             {-10000000, -4110899}, | ||||
|             {-9945219, -3065619}, | ||||
|         }, | ||||
|         { | ||||
|             {-18000000, -1000000}, | ||||
|             {-15000000, 22000000}, | ||||
|             {-11000000, 26000000}, | ||||
|             {11000000, 26000000}, | ||||
|             {15000000, 22000000}, | ||||
|             {18000000, -1000000}, | ||||
|             {18000000, -26000000}, | ||||
|             {-18000000, -26000000}, | ||||
|             {-18000000, -1000000}, | ||||
|         }, | ||||
|     }; | ||||
| 
 | ||||
|     std::vector<Item> proba = { | ||||
|         { | ||||
|             Rectangle(100, 2) | ||||
|         }, | ||||
|         { | ||||
|             Rectangle(100, 2) | ||||
|         }, | ||||
|         { | ||||
|             Rectangle(100, 2) | ||||
|         }, | ||||
|         { | ||||
|             Rectangle(10, 10) | ||||
|         }, | ||||
|     }; | ||||
| 
 | ||||
|     proba[0].rotate(Pi/3); | ||||
|     proba[1].rotate(Pi-Pi/3); | ||||
| 
 | ||||
| //    std::vector<Item> input(25, Rectangle(70*SCALE, 10*SCALE));
 | ||||
|     std::vector<Item> input; | ||||
|     input.insert(input.end(), prusaParts().begin(), prusaParts().end()); | ||||
| //    input.insert(input.end(), prusaExParts().begin(), prusaExParts().end());
 | ||||
| //    input.insert(input.end(), stegoParts().begin(), stegoParts().end());
 | ||||
| //    input.insert(input.end(), rects.begin(), rects.end());
 | ||||
| //    input.insert(input.end(), proba.begin(), proba.end());
 | ||||
| //    input.insert(input.end(), crasher.begin(), crasher.end());
 | ||||
| 
 | ||||
|     Box bin(250*SCALE, 210*SCALE); | ||||
| //    PolygonImpl bin = {
 | ||||
|  | @ -560,10 +121,12 @@ void arrangeRectangles() { | |||
| //        {}
 | ||||
| //    };
 | ||||
| 
 | ||||
|     auto min_obj_distance = static_cast<Coord>(0*SCALE); | ||||
| //    Circle bin({0, 0}, 125*SCALE);
 | ||||
| 
 | ||||
|     using Placer = strategies::_NofitPolyPlacer<PolygonImpl, Box>; | ||||
|     using Packer = Arranger<Placer, FirstFitSelection>; | ||||
|     auto min_obj_distance = static_cast<Coord>(6*SCALE); | ||||
| 
 | ||||
|     using Placer = placers::_NofitPolyPlacer<PolygonImpl, decltype(bin)>; | ||||
|     using Packer = Nester<Placer, FirstFitSelection>; | ||||
| 
 | ||||
|     Packer arrange(bin, min_obj_distance); | ||||
| 
 | ||||
|  | @ -571,121 +134,23 @@ void arrangeRectangles() { | |||
|     pconf.alignment = Placer::Config::Alignment::CENTER; | ||||
|     pconf.starting_point = Placer::Config::Alignment::CENTER; | ||||
|     pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/}; | ||||
|     pconf.accuracy = 0.5f; | ||||
| 
 | ||||
| //    auto bincenter = ShapeLike::boundingBox(bin).center();
 | ||||
| //    pconf.object_function = [&bin, bincenter](
 | ||||
| //            Placer::Pile pile, const Item& item,
 | ||||
| //            double /*area*/, double norm, double penality) {
 | ||||
| 
 | ||||
| //        using pl = PointLike;
 | ||||
| 
 | ||||
| //        static const double BIG_ITEM_TRESHOLD = 0.2;
 | ||||
| //        static const double GRAVITY_RATIO = 0.5;
 | ||||
| //        static const double DENSITY_RATIO = 1.0 - GRAVITY_RATIO;
 | ||||
| 
 | ||||
| //        // We will treat big items (compared to the print bed) differently
 | ||||
| //        NfpPlacer::Pile bigs;
 | ||||
| //        bigs.reserve(pile.size());
 | ||||
| //        for(auto& p : pile) {
 | ||||
| //            auto pbb = ShapeLike::boundingBox(p);
 | ||||
| //            auto na = std::sqrt(pbb.width()*pbb.height())/norm;
 | ||||
| //            if(na > BIG_ITEM_TRESHOLD) bigs.emplace_back(p);
 | ||||
| //        }
 | ||||
| 
 | ||||
| //        // Candidate item bounding box
 | ||||
| //        auto ibb = item.boundingBox();
 | ||||
| 
 | ||||
| //        // Calculate the full bounding box of the pile with the candidate item
 | ||||
| //        pile.emplace_back(item.transformedShape());
 | ||||
| //        auto fullbb = ShapeLike::boundingBox(pile);
 | ||||
| //        pile.pop_back();
 | ||||
| 
 | ||||
| //        // The bounding box of the big items (they will accumulate in the center
 | ||||
| //        // of the pile
 | ||||
| //        auto bigbb = bigs.empty()? fullbb : ShapeLike::boundingBox(bigs);
 | ||||
| 
 | ||||
| //        // The size indicator of the candidate item. This is not the area,
 | ||||
| //        // but almost...
 | ||||
| //        auto itemnormarea = std::sqrt(ibb.width()*ibb.height())/norm;
 | ||||
| 
 | ||||
| //        // Will hold the resulting score
 | ||||
| //        double score = 0;
 | ||||
| 
 | ||||
| //        if(itemnormarea > BIG_ITEM_TRESHOLD) {
 | ||||
| //            // This branch is for the bigger items..
 | ||||
| //            // Here we will use the closest point of the item bounding box to
 | ||||
| //            // the already arranged pile. So not the bb center nor the a choosen
 | ||||
| //            // corner but whichever is the closest to the center. This will
 | ||||
| //            // prevent unwanted strange arrangements.
 | ||||
| 
 | ||||
| //            auto minc = ibb.minCorner(); // bottom left corner
 | ||||
| //            auto maxc = ibb.maxCorner(); // top right corner
 | ||||
| 
 | ||||
| //            // top left and bottom right corners
 | ||||
| //            auto top_left = PointImpl{getX(minc), getY(maxc)};
 | ||||
| //            auto bottom_right = PointImpl{getX(maxc), getY(minc)};
 | ||||
| 
 | ||||
| //            auto cc = fullbb.center(); // The gravity center
 | ||||
| 
 | ||||
| //            // Now the distnce of the gravity center will be calculated to the
 | ||||
| //            // five anchor points and the smallest will be chosen.
 | ||||
| //            std::array<double, 5> dists;
 | ||||
| //            dists[0] = pl::distance(minc, cc);
 | ||||
| //            dists[1] = pl::distance(maxc, cc);
 | ||||
| //            dists[2] = pl::distance(ibb.center(), cc);
 | ||||
| //            dists[3] = pl::distance(top_left, cc);
 | ||||
| //            dists[4] = pl::distance(bottom_right, cc);
 | ||||
| 
 | ||||
| //            auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
 | ||||
| 
 | ||||
| //            // Density is the pack density: how big is the arranged pile
 | ||||
| //            auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
 | ||||
| 
 | ||||
| //            // The score is a weighted sum of the distance from pile center
 | ||||
| //            // and the pile size
 | ||||
| //            score = GRAVITY_RATIO * dist + DENSITY_RATIO * density;
 | ||||
| 
 | ||||
| //        } else if(itemnormarea < BIG_ITEM_TRESHOLD && bigs.empty()) {
 | ||||
| //            // If there are no big items, only small, we should consider the
 | ||||
| //            // density here as well to not get silly results
 | ||||
| //            auto bindist = pl::distance(ibb.center(), bincenter) / norm;
 | ||||
| //            auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
 | ||||
| //            score = GRAVITY_RATIO * bindist + DENSITY_RATIO * density;
 | ||||
| //        } else {
 | ||||
| //            // Here there are the small items that should be placed around the
 | ||||
| //            // already processed bigger items.
 | ||||
| //            // No need to play around with the anchor points, the center will be
 | ||||
| //            // just fine for small items
 | ||||
| //            score = pl::distance(ibb.center(), bigbb.center()) / norm;
 | ||||
| //        }
 | ||||
| 
 | ||||
| //        // If it does not fit into the print bed we will beat it
 | ||||
| //        // with a large penality. If we would not do this, there would be only
 | ||||
| //        // one big pile that doesn't care whether it fits onto the print bed.
 | ||||
| //        if(!NfpPlacer::wouldFit(fullbb, bin)) score = 2*penality - score;
 | ||||
| 
 | ||||
| //        return score;
 | ||||
| //    };
 | ||||
|     pconf.accuracy = 0.65f; | ||||
|     pconf.parallel = true; | ||||
| 
 | ||||
|     Packer::SelectionConfig sconf; | ||||
| //    sconf.allow_parallel = false;
 | ||||
| //    sconf.force_parallel = false;
 | ||||
| //    sconf.try_triplets = true;
 | ||||
| //    sconf.try_reverse_order = true;
 | ||||
| //    sconf.waste_increment = 0.005;
 | ||||
| //    sconf.waste_increment = 0.01;
 | ||||
| 
 | ||||
|     arrange.configure(pconf, sconf); | ||||
| 
 | ||||
|     arrange.progressIndicator([&](unsigned r){ | ||||
| //        svg::SVGWriter::Config conf;
 | ||||
| //        conf.mm_in_coord_units = SCALE;
 | ||||
| //        svg::SVGWriter svgw(conf);
 | ||||
| //        svgw.setSize(bin);
 | ||||
| //        svgw.writePackGroup(arrange.lastResult());
 | ||||
| //        svgw.save("debout");
 | ||||
|         std::cout << "Remaining items: " << r << std::endl; | ||||
|     })/*.useMinimumBoundigBoxRotation()*/; | ||||
|     }); | ||||
| 
 | ||||
| //    findMinimumBoundingBoxRotations(input.begin(), input.end());
 | ||||
| 
 | ||||
|     Benchmark bench; | ||||
| 
 | ||||
|  | @ -693,7 +158,7 @@ void arrangeRectangles() { | |||
|     Packer::ResultType result; | ||||
| 
 | ||||
|     try { | ||||
|         result = arrange.arrange(input.begin(), input.end()); | ||||
|         result = arrange.execute(input.begin(), input.end()); | ||||
|     } catch(GeometryException& ge) { | ||||
|         std::cerr << "Geometry error: " << ge.what() << std::endl; | ||||
|         return ; | ||||
|  | @ -707,7 +172,7 @@ void arrangeRectangles() { | |||
|     std::vector<double> eff; | ||||
|     eff.reserve(result.size()); | ||||
| 
 | ||||
|     auto bin_area = ShapeLike::area<PolygonImpl>(bin); | ||||
|     auto bin_area = sl::area(bin); | ||||
|     for(auto& r : result) { | ||||
|         double a = 0; | ||||
|         std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); }); | ||||
|  | @ -728,10 +193,10 @@ void arrangeRectangles() { | |||
|     for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); } | ||||
|     std::cout << ") Total: " << total << std::endl; | ||||
| 
 | ||||
|     for(auto& it : input) { | ||||
|         auto ret = ShapeLike::isValid(it.transformedShape()); | ||||
|         std::cout << ret.second << std::endl; | ||||
|     } | ||||
| //    for(auto& it : input) {
 | ||||
| //        auto ret = sl::isValid(it.transformedShape());
 | ||||
| //        std::cout << ret.second << std::endl;
 | ||||
| //    }
 | ||||
| 
 | ||||
|     if(total != input.size()) std::cout << "ERROR " << "could not pack " | ||||
|                                         << input.size() - total << " elements!" | ||||
|  | @ -744,13 +209,10 @@ void arrangeRectangles() { | |||
|     SVGWriter svgw(conf); | ||||
|     svgw.setSize(Box(250*SCALE, 210*SCALE)); | ||||
|     svgw.writePackGroup(result); | ||||
| //    std::for_each(input.begin(), input.end(), [&svgw](Item& item){ svgw.writeItem(item);});
 | ||||
|     svgw.save("out"); | ||||
| } | ||||
| 
 | ||||
| int main(void /*int argc, char **argv*/) { | ||||
|     arrangeRectangles(); | ||||
| //    findDegenerateCase();
 | ||||
| 
 | ||||
|     return EXIT_SUCCESS; | ||||
| } | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ using Point = PointImpl; | |||
| using Coord = TCoord<PointImpl>; | ||||
| using Box = _Box<PointImpl>; | ||||
| using Segment = _Segment<PointImpl>; | ||||
| using Circle = _Circle<PointImpl>; | ||||
| 
 | ||||
| using Item = _Item<PolygonImpl>; | ||||
| using Rectangle = _Rectangle<PolygonImpl>; | ||||
|  | @ -29,15 +30,12 @@ using Rectangle = _Rectangle<PolygonImpl>; | |||
| using PackGroup = _PackGroup<PolygonImpl>; | ||||
| using IndexedPackGroup = _IndexedPackGroup<PolygonImpl>; | ||||
| 
 | ||||
| using FillerSelection = strategies::_FillerSelection<PolygonImpl>; | ||||
| using FirstFitSelection = strategies::_FirstFitSelection<PolygonImpl>; | ||||
| using DJDHeuristic  = strategies::_DJDHeuristic<PolygonImpl>; | ||||
| using FillerSelection = selections::_FillerSelection<PolygonImpl>; | ||||
| using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>; | ||||
| using DJDHeuristic  = selections::_DJDHeuristic<PolygonImpl>; | ||||
| 
 | ||||
| using NfpPlacer = strategies::_NofitPolyPlacer<PolygonImpl>; | ||||
| using BottomLeftPlacer = strategies::_BottomLeftPlacer<PolygonImpl>; | ||||
| 
 | ||||
| //template<NfpLevel lvl = NfpLevel::BOTH_CONCAVE_WITH_HOLES>
 | ||||
| //using NofitPolyPlacer = strategies::_NofitPolyPlacer<PolygonImpl, lvl>;
 | ||||
| using NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl>; | ||||
| using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ using libnest2d::setX; | |||
| using libnest2d::setY; | ||||
| using Box = libnest2d::_Box<PointImpl>; | ||||
| using Segment = libnest2d::_Segment<PointImpl>; | ||||
| using Shapes = libnest2d::Nfp::Shapes<PolygonImpl>; | ||||
| using Shapes = libnest2d::nfp::Shapes<PolygonImpl>; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  | @ -241,11 +241,11 @@ template<> struct tag<bp2d::PolygonImpl> { | |||
| 
 | ||||
| template<> struct exterior_ring<bp2d::PolygonImpl> { | ||||
|     static inline bp2d::PathImpl& get(bp2d::PolygonImpl& p) { | ||||
|         return libnest2d::ShapeLike::getContour(p); | ||||
|         return libnest2d::shapelike::getContour(p); | ||||
|     } | ||||
| 
 | ||||
|     static inline bp2d::PathImpl const& get(bp2d::PolygonImpl const& p) { | ||||
|         return libnest2d::ShapeLike::getContour(p); | ||||
|         return libnest2d::shapelike::getContour(p); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  | @ -271,13 +271,13 @@ struct interior_rings<bp2d::PolygonImpl> { | |||
|     static inline libnest2d::THolesContainer<bp2d::PolygonImpl>& get( | ||||
|             bp2d::PolygonImpl& p) | ||||
|     { | ||||
|         return libnest2d::ShapeLike::holes(p); | ||||
|         return libnest2d::shapelike::holes(p); | ||||
|     } | ||||
| 
 | ||||
|     static inline const libnest2d::THolesContainer<bp2d::PolygonImpl>& get( | ||||
|             bp2d::PolygonImpl const& p) | ||||
|     { | ||||
|         return libnest2d::ShapeLike::holes(p); | ||||
|         return libnest2d::shapelike::holes(p); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  | @ -311,83 +311,77 @@ struct range_value<bp2d::Shapes> { | |||
| 
 | ||||
| namespace libnest2d { // Now the algorithms that boost can provide...
 | ||||
| 
 | ||||
| namespace pointlike { | ||||
| template<> | ||||
| inline double PointLike::distance(const PointImpl& p1, | ||||
|                                   const PointImpl& p2 ) | ||||
| inline double distance(const PointImpl& p1, const PointImpl& p2 ) | ||||
| { | ||||
|     return boost::geometry::distance(p1, p2); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline double PointLike::distance(const PointImpl& p, | ||||
|                                   const bp2d::Segment& seg ) | ||||
| inline double distance(const PointImpl& p, const bp2d::Segment& seg ) | ||||
| { | ||||
|     return boost::geometry::distance(p, seg); | ||||
| } | ||||
| } | ||||
| 
 | ||||
| namespace shapelike { | ||||
| // Tell libnest2d how to make string out of a ClipperPolygon object
 | ||||
| template<> | ||||
| inline bool ShapeLike::intersects(const PathImpl& sh1, | ||||
|                                   const PathImpl& sh2) | ||||
| inline bool intersects(const PathImpl& sh1, const PathImpl& sh2) | ||||
| { | ||||
|     return boost::geometry::intersects(sh1, sh2); | ||||
| } | ||||
| 
 | ||||
| // Tell libnest2d how to make string out of a ClipperPolygon object
 | ||||
| template<> | ||||
| inline bool ShapeLike::intersects(const PolygonImpl& sh1, | ||||
|                                   const PolygonImpl& sh2) | ||||
| inline bool intersects(const PolygonImpl& sh1, const PolygonImpl& sh2) | ||||
| { | ||||
|     return boost::geometry::intersects(sh1, sh2); | ||||
| } | ||||
| 
 | ||||
| // Tell libnest2d how to make string out of a ClipperPolygon object
 | ||||
| template<> | ||||
| inline bool ShapeLike::intersects(const bp2d::Segment& s1, | ||||
|                                   const bp2d::Segment& s2) | ||||
| inline bool intersects(const bp2d::Segment& s1, const bp2d::Segment& s2) | ||||
| { | ||||
|     return boost::geometry::intersects(s1, s2); | ||||
| } | ||||
| 
 | ||||
| #ifndef DISABLE_BOOST_AREA | ||||
| template<> | ||||
| inline double ShapeLike::area(const PolygonImpl& shape) | ||||
| inline double area(const PolygonImpl& shape, const PolygonTag&) | ||||
| { | ||||
|     return boost::geometry::area(shape); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| template<> | ||||
| inline bool ShapeLike::isInside<PolygonImpl>(const PointImpl& point, | ||||
|                                 const PolygonImpl& shape) | ||||
| inline bool isInside(const PointImpl& point, const PolygonImpl& shape) | ||||
| { | ||||
|     return boost::geometry::within(point, shape); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline bool ShapeLike::isInside(const PolygonImpl& sh1, | ||||
|                                 const PolygonImpl& sh2) | ||||
| inline bool isInside(const PolygonImpl& sh1, const PolygonImpl& sh2) | ||||
| { | ||||
|     return boost::geometry::within(sh1, sh2); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline bool ShapeLike::touches( const PolygonImpl& sh1, | ||||
|                                 const PolygonImpl& sh2) | ||||
| inline bool touches(const PolygonImpl& sh1, const PolygonImpl& sh2) | ||||
| { | ||||
|     return boost::geometry::touches(sh1, sh2); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline bool ShapeLike::touches( const PointImpl& point, | ||||
|                                 const PolygonImpl& shape) | ||||
| inline bool touches( const PointImpl& point, const PolygonImpl& shape) | ||||
| { | ||||
|     return boost::geometry::touches(point, shape); | ||||
| } | ||||
| 
 | ||||
| #ifndef DISABLE_BOOST_BOUNDING_BOX | ||||
| template<> | ||||
| inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh) | ||||
| inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&) | ||||
| { | ||||
|     bp2d::Box b; | ||||
|     boost::geometry::envelope(sh, b); | ||||
|  | @ -395,7 +389,8 @@ inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh) | |||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline bp2d::Box ShapeLike::boundingBox<PolygonImpl>(const bp2d::Shapes& shapes) | ||||
| inline bp2d::Box boundingBox<bp2d::Shapes>(const bp2d::Shapes& shapes, | ||||
|                                            const MultiPolygonTag&) | ||||
| { | ||||
|     bp2d::Box b; | ||||
|     boost::geometry::envelope(shapes, b); | ||||
|  | @ -405,7 +400,7 @@ inline bp2d::Box ShapeLike::boundingBox<PolygonImpl>(const bp2d::Shapes& shapes) | |||
| 
 | ||||
| #ifndef DISABLE_BOOST_CONVEX_HULL | ||||
| template<> | ||||
| inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh) | ||||
| inline PolygonImpl convexHull(const PolygonImpl& sh, const PolygonTag&) | ||||
| { | ||||
|     PolygonImpl ret; | ||||
|     boost::geometry::convex_hull(sh, ret); | ||||
|  | @ -413,7 +408,8 @@ inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh) | |||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes) | ||||
| inline PolygonImpl convexHull(const TMultiShape<PolygonImpl>& shapes, | ||||
|                               const MultiPolygonTag&) | ||||
| { | ||||
|     PolygonImpl ret; | ||||
|     boost::geometry::convex_hull(shapes, ret); | ||||
|  | @ -421,56 +417,17 @@ inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes) | |||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifndef DISABLE_BOOST_ROTATE | ||||
| template<> | ||||
| inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads) | ||||
| { | ||||
|     namespace trans = boost::geometry::strategy::transform; | ||||
| 
 | ||||
|     PolygonImpl cpy = sh; | ||||
|     trans::rotate_transformer<boost::geometry::radian, Radians, 2, 2> | ||||
|             rotate(rads); | ||||
| 
 | ||||
|     boost::geometry::transform(cpy, sh, rotate); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifndef DISABLE_BOOST_TRANSLATE | ||||
| template<> | ||||
| inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs) | ||||
| { | ||||
|     namespace trans = boost::geometry::strategy::transform; | ||||
| 
 | ||||
|     PolygonImpl cpy = sh; | ||||
|     trans::translate_transformer<bp2d::Coord, 2, 2> translate( | ||||
|                 bp2d::getX(offs), bp2d::getY(offs)); | ||||
| 
 | ||||
|     boost::geometry::transform(cpy, sh, translate); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifndef DISABLE_BOOST_OFFSET | ||||
| template<> | ||||
| inline void ShapeLike::offset(PolygonImpl& sh, bp2d::Coord distance) | ||||
| inline void offset(PolygonImpl& sh, bp2d::Coord distance) | ||||
| { | ||||
|     PolygonImpl cpy = sh; | ||||
|     boost::geometry::buffer(cpy, sh, distance); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifndef DISABLE_BOOST_NFP_MERGE | ||||
| template<> | ||||
| inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes, | ||||
|                                const PolygonImpl& sh) | ||||
| { | ||||
|     bp2d::Shapes retv; | ||||
|     boost::geometry::union_(shapes, sh, retv); | ||||
|     return retv; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifndef DISABLE_BOOST_SERIALIZE | ||||
| template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>( | ||||
| template<> inline std::string serialize<libnest2d::Formats::SVG>( | ||||
|         const PolygonImpl& sh, double scale) | ||||
| { | ||||
|     std::stringstream ss; | ||||
|  | @ -482,14 +439,14 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>( | |||
| 
 | ||||
|     Polygonf::ring_type ring; | ||||
|     Polygonf::inner_container_type holes; | ||||
|     ring.reserve(ShapeLike::contourVertexCount(sh)); | ||||
|     ring.reserve(shapelike::contourVertexCount(sh)); | ||||
| 
 | ||||
|     for(auto it = ShapeLike::cbegin(sh); it != ShapeLike::cend(sh); it++) { | ||||
|     for(auto it = shapelike::cbegin(sh); it != shapelike::cend(sh); it++) { | ||||
|         auto& v = *it; | ||||
|         ring.emplace_back(getX(v)*scale, getY(v)*scale); | ||||
|     }; | ||||
| 
 | ||||
|     auto H = ShapeLike::holes(sh); | ||||
|     auto H = shapelike::holes(sh); | ||||
|     for(PathImpl& h : H ) { | ||||
|         Polygonf::ring_type hf; | ||||
|         for(auto it = h.begin(); it != h.end(); it++) { | ||||
|  | @ -512,21 +469,47 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>( | |||
| 
 | ||||
| #ifndef DISABLE_BOOST_UNSERIALIZE | ||||
| template<> | ||||
| inline void ShapeLike::unserialize<libnest2d::Formats::SVG>( | ||||
| inline void unserialize<libnest2d::Formats::SVG>( | ||||
|         PolygonImpl& sh, | ||||
|         const std::string& str) | ||||
| { | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| template<> inline std::pair<bool, std::string> | ||||
| ShapeLike::isValid(const PolygonImpl& sh) | ||||
| template<> inline std::pair<bool, std::string> isValid(const PolygonImpl& sh) | ||||
| { | ||||
|     std::string message; | ||||
|     bool ret = boost::geometry::is_valid(sh, message); | ||||
| 
 | ||||
|     return {ret, message}; | ||||
| } | ||||
| } | ||||
| 
 | ||||
| namespace nfp { | ||||
| 
 | ||||
| #ifndef DISABLE_BOOST_NFP_MERGE | ||||
| 
 | ||||
| // Warning: I could not get boost union_ to work. Geometries will overlap.
 | ||||
| template<> | ||||
| inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes, | ||||
|                                const PolygonImpl& sh) | ||||
| { | ||||
|     bp2d::Shapes retv; | ||||
|     boost::geometry::union_(shapes, sh, retv); | ||||
|     return retv; | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes) | ||||
| { | ||||
|     bp2d::Shapes retv; | ||||
|     boost::geometry::union_(shapes, shapes.back(), retv); | ||||
|     return retv; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -99,49 +99,54 @@ template<> struct PointType<PolygonImpl> { | |||
|     using Type = PointImpl; | ||||
| }; | ||||
| 
 | ||||
| // Type of vertex iterator used by Clipper
 | ||||
| template<> struct VertexIteratorType<PolygonImpl> { | ||||
|     using Type = ClipperLib::Path::iterator; | ||||
| }; | ||||
| 
 | ||||
| // Type of vertex iterator used by Clipper
 | ||||
| template<> struct VertexConstIteratorType<PolygonImpl> { | ||||
|     using Type = ClipperLib::Path::const_iterator; | ||||
| template<> struct PointType<PointImpl> { | ||||
|     using Type = PointImpl; | ||||
| }; | ||||
| 
 | ||||
| template<> struct CountourType<PolygonImpl> { | ||||
|     using Type = PathImpl; | ||||
| }; | ||||
| 
 | ||||
| // Tell binpack2d how to extract the X coord from a ClipperPoint object
 | ||||
| template<> inline TCoord<PointImpl> PointLike::x(const PointImpl& p) | ||||
| template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; }; | ||||
| 
 | ||||
| template<> struct ShapeTag<TMultiShape<PolygonImpl>> { | ||||
|     using Type = MultiPolygonTag; | ||||
| }; | ||||
| 
 | ||||
| template<> struct PointType<TMultiShape<PolygonImpl>> { | ||||
|     using Type = PointImpl; | ||||
| }; | ||||
| 
 | ||||
| template<> struct HolesContainer<PolygonImpl> { | ||||
|     using Type = ClipperLib::Paths; | ||||
| }; | ||||
| 
 | ||||
| namespace pointlike { | ||||
| 
 | ||||
| // Tell libnest2d how to extract the X coord from a ClipperPoint object
 | ||||
| template<> inline TCoord<PointImpl> x(const PointImpl& p) | ||||
| { | ||||
|     return p.X; | ||||
| } | ||||
| 
 | ||||
| // Tell binpack2d how to extract the Y coord from a ClipperPoint object
 | ||||
| template<> inline TCoord<PointImpl> PointLike::y(const PointImpl& p) | ||||
| // Tell libnest2d how to extract the Y coord from a ClipperPoint object
 | ||||
| template<> inline TCoord<PointImpl> y(const PointImpl& p) | ||||
| { | ||||
|     return p.Y; | ||||
| } | ||||
| 
 | ||||
| // Tell binpack2d how to extract the X coord from a ClipperPoint object
 | ||||
| template<> inline TCoord<PointImpl>& PointLike::x(PointImpl& p) | ||||
| // Tell libnest2d how to extract the X coord from a ClipperPoint object
 | ||||
| template<> inline TCoord<PointImpl>& x(PointImpl& p) | ||||
| { | ||||
|     return p.X; | ||||
| } | ||||
| 
 | ||||
| // Tell binpack2d how to extract the Y coord from a ClipperPoint object
 | ||||
| template<> | ||||
| inline TCoord<PointImpl>& PointLike::y(PointImpl& p) | ||||
| // Tell libnest2d how to extract the Y coord from a ClipperPoint object
 | ||||
| template<> inline TCoord<PointImpl>& y(PointImpl& p) | ||||
| { | ||||
|     return p.Y; | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline void ShapeLike::reserve(PolygonImpl& sh, size_t vertex_capacity) | ||||
| { | ||||
|     return sh.Contour.reserve(vertex_capacity); | ||||
| } | ||||
| 
 | ||||
| #define DISABLE_BOOST_AREA | ||||
|  | @ -175,16 +180,24 @@ inline double area<Orientation::COUNTER_CLOCKWISE>(const PolygonImpl& sh) { | |||
| 
 | ||||
|     return ClipperLib::Area(sh.Contour) + a; | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // Tell binpack2d how to make string out of a ClipperPolygon object
 | ||||
| template<> | ||||
| inline double ShapeLike::area(const PolygonImpl& sh) { | ||||
| namespace shapelike { | ||||
| 
 | ||||
| template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity) | ||||
| { | ||||
|     return sh.Contour.reserve(vertex_capacity); | ||||
| } | ||||
| 
 | ||||
| // Tell libnest2d how to make string out of a ClipperPolygon object
 | ||||
| template<> inline double area(const PolygonImpl& sh, const PolygonTag&) | ||||
| { | ||||
|     return _smartarea::area<OrientationType<PolygonImpl>::Value>(sh); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) { | ||||
| template<> inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance) | ||||
| { | ||||
|     #define DISABLE_BOOST_OFFSET | ||||
| 
 | ||||
|     using ClipperLib::ClipperOffset; | ||||
|  | @ -234,7 +247,8 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) { | |||
| } | ||||
| 
 | ||||
| // Tell libnest2d how to make string out of a ClipperPolygon object
 | ||||
| template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) { | ||||
| template<> inline std::string toString(const PolygonImpl& sh) | ||||
| { | ||||
|     std::stringstream ss; | ||||
| 
 | ||||
|     ss << "Contour {\n"; | ||||
|  | @ -257,37 +271,8 @@ template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) { | |||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline TVertexIterator<PolygonImpl> ShapeLike::begin(PolygonImpl& sh) | ||||
| inline PolygonImpl create(const PathImpl& path, const HoleStore& holes) | ||||
| { | ||||
|     return sh.Contour.begin(); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline TVertexIterator<PolygonImpl> ShapeLike::end(PolygonImpl& sh) | ||||
| { | ||||
|     return sh.Contour.end(); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline TVertexConstIterator<PolygonImpl> ShapeLike::cbegin( | ||||
|         const PolygonImpl& sh) | ||||
| { | ||||
|     return sh.Contour.cbegin(); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline TVertexConstIterator<PolygonImpl> ShapeLike::cend( | ||||
|         const PolygonImpl& sh) | ||||
| { | ||||
|     return sh.Contour.cend(); | ||||
| } | ||||
| 
 | ||||
| template<> struct HolesContainer<PolygonImpl> { | ||||
|     using Type = ClipperLib::Paths; | ||||
| }; | ||||
| 
 | ||||
| template<> inline PolygonImpl ShapeLike::create(const PathImpl& path, | ||||
|                                                 const HoleStore& holes) { | ||||
|     PolygonImpl p; | ||||
|     p.Contour = path; | ||||
| 
 | ||||
|  | @ -308,8 +293,7 @@ template<> inline PolygonImpl ShapeLike::create(const PathImpl& path, | |||
|     return p; | ||||
| } | ||||
| 
 | ||||
| template<> inline PolygonImpl ShapeLike::create( PathImpl&& path, | ||||
|                                                  HoleStore&& holes) { | ||||
| template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) { | ||||
|     PolygonImpl p; | ||||
|     p.Contour.swap(path); | ||||
| 
 | ||||
|  | @ -331,49 +315,49 @@ template<> inline PolygonImpl ShapeLike::create( PathImpl&& path, | |||
|     return p; | ||||
| } | ||||
| 
 | ||||
| template<> inline const THolesContainer<PolygonImpl>& | ||||
| ShapeLike::holes(const PolygonImpl& sh) | ||||
| template<> | ||||
| inline const THolesContainer<PolygonImpl>& holes(const PolygonImpl& sh) | ||||
| { | ||||
|     return sh.Holes; | ||||
| } | ||||
| 
 | ||||
| template<> inline THolesContainer<PolygonImpl>& | ||||
| ShapeLike::holes(PolygonImpl& sh) | ||||
| template<> inline THolesContainer<PolygonImpl>& holes(PolygonImpl& sh) | ||||
| { | ||||
|     return sh.Holes; | ||||
| } | ||||
| 
 | ||||
| template<> inline TContour<PolygonImpl>& | ||||
| ShapeLike::getHole(PolygonImpl& sh, unsigned long idx) | ||||
| template<> | ||||
| inline TContour<PolygonImpl>& getHole(PolygonImpl& sh, unsigned long idx) | ||||
| { | ||||
|     return sh.Holes[idx]; | ||||
| } | ||||
| 
 | ||||
| template<> inline const TContour<PolygonImpl>& | ||||
| ShapeLike::getHole(const PolygonImpl& sh, unsigned long idx) | ||||
| template<> | ||||
| inline const TContour<PolygonImpl>& getHole(const PolygonImpl& sh, | ||||
|                                             unsigned long idx) | ||||
| { | ||||
|     return sh.Holes[idx]; | ||||
| } | ||||
| 
 | ||||
| template<> inline size_t ShapeLike::holeCount(const PolygonImpl& sh) | ||||
| template<> inline size_t holeCount(const PolygonImpl& sh) | ||||
| { | ||||
|     return sh.Holes.size(); | ||||
| } | ||||
| 
 | ||||
| template<> inline PathImpl& ShapeLike::getContour(PolygonImpl& sh) | ||||
| template<> inline PathImpl& getContour(PolygonImpl& sh) | ||||
| { | ||||
|     return sh.Contour; | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline const PathImpl& ShapeLike::getContour(const PolygonImpl& sh) | ||||
| inline const PathImpl& getContour(const PolygonImpl& sh) | ||||
| { | ||||
|     return sh.Contour; | ||||
| } | ||||
| 
 | ||||
| #define DISABLE_BOOST_TRANSLATE | ||||
| template<> | ||||
| inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs) | ||||
| inline void translate(PolygonImpl& sh, const PointImpl& offs) | ||||
| { | ||||
|     for(auto& p : sh.Contour) { p += offs; } | ||||
|     for(auto& hole : sh.Holes) for(auto& p : hole) { p += offs; } | ||||
|  | @ -381,7 +365,7 @@ inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs) | |||
| 
 | ||||
| #define DISABLE_BOOST_ROTATE | ||||
| template<> | ||||
| inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads) | ||||
| inline void rotate(PolygonImpl& sh, const Radians& rads) | ||||
| { | ||||
|     using Coord = TCoord<PointImpl>; | ||||
| 
 | ||||
|  | @ -402,9 +386,11 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace shapelike
 | ||||
| 
 | ||||
| #define DISABLE_BOOST_NFP_MERGE | ||||
| inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) { | ||||
|     Nfp::Shapes<PolygonImpl> retv; | ||||
| inline std::vector<PolygonImpl> _merge(ClipperLib::Clipper& clipper) { | ||||
|     shapelike::Shapes<PolygonImpl> retv; | ||||
| 
 | ||||
|     ClipperLib::PolyTree result; | ||||
|     clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative); | ||||
|  | @ -438,8 +424,10 @@ inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) { | |||
|     return retv; | ||||
| } | ||||
| 
 | ||||
| template<> inline Nfp::Shapes<PolygonImpl> | ||||
| Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes) | ||||
| namespace nfp { | ||||
| 
 | ||||
| template<> inline std::vector<PolygonImpl> | ||||
| merge(const std::vector<PolygonImpl>& shapes) | ||||
| { | ||||
|     ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution); | ||||
| 
 | ||||
|  | @ -461,6 +449,8 @@ Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes) | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| //#define DISABLE_BOOST_SERIALIZE
 | ||||
| //#define DISABLE_BOOST_UNSERIALIZE
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,34 +22,12 @@ template<class GeomType> | |||
| using TCoord = typename CoordType<remove_cvref_t<GeomType>>::Type; | ||||
| 
 | ||||
| /// Getting the type of point structure used by a shape.
 | ||||
| template<class Shape> struct PointType { /*using Type = void;*/ }; | ||||
| template<class Sh> struct PointType { using Type = typename Sh::PointType; }; | ||||
| 
 | ||||
| /// TPoint<ShapeClass> as shorthand for `typename PointType<ShapeClass>::Type`.
 | ||||
| template<class Shape> | ||||
| using TPoint = typename PointType<remove_cvref_t<Shape>>::Type; | ||||
| 
 | ||||
| /// Getting the VertexIterator type of a shape class.
 | ||||
| template<class Shape> struct VertexIteratorType { /*using Type = void;*/ }; | ||||
| 
 | ||||
| /// Getting the const vertex iterator for a shape class.
 | ||||
| template<class Shape> struct VertexConstIteratorType {/* using Type = void;*/ }; | ||||
| 
 | ||||
| /**
 | ||||
|  * TVertexIterator<Shape> as shorthand for | ||||
|  * `typename VertexIteratorType<Shape>::Type` | ||||
|  */ | ||||
| template<class Shape> | ||||
| using TVertexIterator = | ||||
| typename VertexIteratorType<remove_cvref_t<Shape>>::Type; | ||||
| 
 | ||||
| /**
 | ||||
|  * \brief TVertexConstIterator<Shape> as shorthand for | ||||
|  * `typename VertexConstIteratorType<Shape>::Type` | ||||
|  */ | ||||
| template<class ShapeClass> | ||||
| using TVertexConstIterator = | ||||
| typename VertexConstIteratorType<remove_cvref_t<ShapeClass>>::Type; | ||||
| 
 | ||||
| /**
 | ||||
|  * \brief A point pair base class for other point pairs (segment, box, ...). | ||||
|  * \tparam RawPoint The actual point type to use. | ||||
|  | @ -60,6 +38,17 @@ struct PointPair { | |||
|     RawPoint p2; | ||||
| }; | ||||
| 
 | ||||
| struct PolygonTag {}; | ||||
| struct MultiPolygonTag {}; | ||||
| struct BoxTag {}; | ||||
| struct CircleTag {}; | ||||
| 
 | ||||
| template<class Shape> struct ShapeTag { using Type = typename Shape::Tag; }; | ||||
| template<class S> using Tag = typename ShapeTag<S>::Type; | ||||
| 
 | ||||
| template<class S> struct MultiShape { using Type = std::vector<S>; }; | ||||
| template<class S> using TMultiShape = typename MultiShape<S>::Type; | ||||
| 
 | ||||
| /**
 | ||||
|  * \brief An abstraction of a box; | ||||
|  */ | ||||
|  | @ -69,6 +58,9 @@ class _Box: PointPair<RawPoint> { | |||
|     using PointPair<RawPoint>::p2; | ||||
| public: | ||||
| 
 | ||||
|     using Tag = BoxTag; | ||||
|     using PointType = RawPoint; | ||||
| 
 | ||||
|     inline _Box() = default; | ||||
|     inline _Box(const RawPoint& p, const RawPoint& pp): | ||||
|         PointPair<RawPoint>({p, pp}) {} | ||||
|  | @ -98,6 +90,9 @@ class _Circle { | |||
|     double radius_ = 0; | ||||
| public: | ||||
| 
 | ||||
|     using Tag = CircleTag; | ||||
|     using PointType = RawPoint; | ||||
| 
 | ||||
|     _Circle() = default; | ||||
| 
 | ||||
|     _Circle(const RawPoint& center, double r): center_(center), radius_(r) {} | ||||
|  | @ -109,7 +104,7 @@ public: | |||
|     inline void radius(double r) { radius_ = r; } | ||||
| 
 | ||||
|     inline double area() const BP2D_NOEXCEPT { | ||||
|         return 2.0*Pi*radius_; | ||||
|         return 2.0*Pi*radius_*radius_; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  | @ -123,6 +118,8 @@ class _Segment: PointPair<RawPoint> { | |||
|     mutable Radians angletox_ = std::nan(""); | ||||
| public: | ||||
| 
 | ||||
|     using PointType = RawPoint; | ||||
| 
 | ||||
|     inline _Segment() = default; | ||||
| 
 | ||||
|     inline _Segment(const RawPoint& p, const RawPoint& pp): | ||||
|  | @ -156,36 +153,36 @@ public: | |||
|     inline double length(); | ||||
| }; | ||||
| 
 | ||||
| // This struct serves as a namespace. The only difference is that is can be
 | ||||
| // This struct serves almost as a namespace. The only difference is that is can
 | ||||
| // used in friend declarations.
 | ||||
| struct PointLike { | ||||
| namespace pointlike { | ||||
| 
 | ||||
|     template<class RawPoint> | ||||
|     static TCoord<RawPoint> x(const RawPoint& p) | ||||
|     inline TCoord<RawPoint> x(const RawPoint& p) | ||||
|     { | ||||
|         return p.x(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawPoint> | ||||
|     static TCoord<RawPoint> y(const RawPoint& p) | ||||
|     inline TCoord<RawPoint> y(const RawPoint& p) | ||||
|     { | ||||
|         return p.y(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawPoint> | ||||
|     static TCoord<RawPoint>& x(RawPoint& p) | ||||
|     inline TCoord<RawPoint>& x(RawPoint& p) | ||||
|     { | ||||
|         return p.x(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawPoint> | ||||
|     static TCoord<RawPoint>& y(RawPoint& p) | ||||
|     inline TCoord<RawPoint>& y(RawPoint& p) | ||||
|     { | ||||
|         return p.y(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawPoint> | ||||
|     static double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/) | ||||
|     inline double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/) | ||||
|     { | ||||
|         static_assert(always_false<RawPoint>::value, | ||||
|                       "PointLike::distance(point, point) unimplemented!"); | ||||
|  | @ -193,7 +190,7 @@ struct PointLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawPoint> | ||||
|     static double distance(const RawPoint& /*p1*/, | ||||
|     inline double distance(const RawPoint& /*p1*/, | ||||
|                            const _Segment<RawPoint>& /*s*/) | ||||
|     { | ||||
|         static_assert(always_false<RawPoint>::value, | ||||
|  | @ -202,13 +199,13 @@ struct PointLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawPoint> | ||||
|     static std::pair<TCoord<RawPoint>, bool> horizontalDistance( | ||||
|     inline std::pair<TCoord<RawPoint>, bool> horizontalDistance( | ||||
|             const RawPoint& p, const _Segment<RawPoint>& s) | ||||
|     { | ||||
|         using Unit = TCoord<RawPoint>; | ||||
|         auto x = PointLike::x(p), y = PointLike::y(p); | ||||
|         auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first()); | ||||
|         auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second()); | ||||
|         auto x = pointlike::x(p), y = pointlike::y(p); | ||||
|         auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first()); | ||||
|         auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second()); | ||||
| 
 | ||||
|         TCoord<RawPoint> ret; | ||||
| 
 | ||||
|  | @ -228,13 +225,13 @@ struct PointLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawPoint> | ||||
|     static std::pair<TCoord<RawPoint>, bool> verticalDistance( | ||||
|     inline std::pair<TCoord<RawPoint>, bool> verticalDistance( | ||||
|             const RawPoint& p, const _Segment<RawPoint>& s) | ||||
|     { | ||||
|         using Unit = TCoord<RawPoint>; | ||||
|         auto x = PointLike::x(p), y = PointLike::y(p); | ||||
|         auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first()); | ||||
|         auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second()); | ||||
|         auto x = pointlike::x(p), y = pointlike::y(p); | ||||
|         auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first()); | ||||
|         auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second()); | ||||
| 
 | ||||
|         TCoord<RawPoint> ret; | ||||
| 
 | ||||
|  | @ -252,36 +249,36 @@ struct PointLike { | |||
| 
 | ||||
|         return {ret, true}; | ||||
|     } | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| template<class RawPoint> | ||||
| TCoord<RawPoint> _Box<RawPoint>::width() const BP2D_NOEXCEPT | ||||
| { | ||||
|     return PointLike::x(maxCorner()) - PointLike::x(minCorner()); | ||||
|     return pointlike::x(maxCorner()) - pointlike::x(minCorner()); | ||||
| } | ||||
| 
 | ||||
| template<class RawPoint> | ||||
| TCoord<RawPoint> _Box<RawPoint>::height() const BP2D_NOEXCEPT | ||||
| { | ||||
|     return PointLike::y(maxCorner()) - PointLike::y(minCorner()); | ||||
|     return pointlike::y(maxCorner()) - pointlike::y(minCorner()); | ||||
| } | ||||
| 
 | ||||
| template<class RawPoint> | ||||
| TCoord<RawPoint> getX(const RawPoint& p) { return PointLike::x<RawPoint>(p); } | ||||
| TCoord<RawPoint> getX(const RawPoint& p) { return pointlike::x<RawPoint>(p); } | ||||
| 
 | ||||
| template<class RawPoint> | ||||
| TCoord<RawPoint> getY(const RawPoint& p) { return PointLike::y<RawPoint>(p); } | ||||
| TCoord<RawPoint> getY(const RawPoint& p) { return pointlike::y<RawPoint>(p); } | ||||
| 
 | ||||
| template<class RawPoint> | ||||
| void setX(RawPoint& p, const TCoord<RawPoint>& val) | ||||
| { | ||||
|     PointLike::x<RawPoint>(p) = val; | ||||
|     pointlike::x<RawPoint>(p) = val; | ||||
| } | ||||
| 
 | ||||
| template<class RawPoint> | ||||
| void setY(RawPoint& p, const TCoord<RawPoint>& val) | ||||
| { | ||||
|     PointLike::y<RawPoint>(p) = val; | ||||
|     pointlike::y<RawPoint>(p) = val; | ||||
| } | ||||
| 
 | ||||
| template<class RawPoint> | ||||
|  | @ -303,7 +300,7 @@ inline Radians _Segment<RawPoint>::angleToXaxis() const | |||
| template<class RawPoint> | ||||
| inline double _Segment<RawPoint>::length() | ||||
| { | ||||
|     return PointLike::distance(first(), second()); | ||||
|     return pointlike::distance(first(), second()); | ||||
| } | ||||
| 
 | ||||
| template<class RawPoint> | ||||
|  | @ -313,9 +310,9 @@ inline RawPoint _Box<RawPoint>::center() const BP2D_NOEXCEPT { | |||
| 
 | ||||
|     using Coord = TCoord<RawPoint>; | ||||
| 
 | ||||
|     RawPoint ret =  { | ||||
|         static_cast<Coord>( std::round((getX(minc) + getX(maxc))/2.0) ), | ||||
|         static_cast<Coord>( std::round((getY(minc) + getY(maxc))/2.0) ) | ||||
|     RawPoint ret =  { // No rounding here, we dont know if these are int coords
 | ||||
|         static_cast<Coord>( (getX(minc) + getX(maxc))/2.0 ), | ||||
|         static_cast<Coord>( (getY(minc) + getY(maxc))/2.0 ) | ||||
|     }; | ||||
| 
 | ||||
|     return ret; | ||||
|  | @ -356,124 +353,125 @@ enum class Formats { | |||
| 
 | ||||
| // This struct serves as a namespace. The only difference is that it can be
 | ||||
| // used in friend declarations and can be aliased at class scope.
 | ||||
| struct ShapeLike { | ||||
| namespace shapelike { | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     using Shapes = std::vector<RawShape>; | ||||
|     using Shapes = TMultiShape<RawShape>; | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static RawShape create(const TContour<RawShape>& contour, | ||||
|     inline RawShape create(const TContour<RawShape>& contour, | ||||
|                            const THolesContainer<RawShape>& holes) | ||||
|     { | ||||
|         return RawShape(contour, holes); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static RawShape create(TContour<RawShape>&& contour, | ||||
|     inline RawShape create(TContour<RawShape>&& contour, | ||||
|                            THolesContainer<RawShape>&& holes) | ||||
|     { | ||||
|         return RawShape(contour, holes); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static RawShape create(const TContour<RawShape>& contour) | ||||
|     inline RawShape create(const TContour<RawShape>& contour) | ||||
|     { | ||||
|         return create<RawShape>(contour, {}); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static RawShape create(TContour<RawShape>&& contour) | ||||
|     inline RawShape create(TContour<RawShape>&& contour) | ||||
|     { | ||||
|         return create<RawShape>(contour, {}); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static THolesContainer<RawShape>& holes(RawShape& /*sh*/) | ||||
|     inline THolesContainer<RawShape>& holes(RawShape& /*sh*/) | ||||
|     { | ||||
|         static THolesContainer<RawShape> empty; | ||||
|         return empty; | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static const THolesContainer<RawShape>& holes(const RawShape& /*sh*/) | ||||
|     inline const THolesContainer<RawShape>& holes(const RawShape& /*sh*/) | ||||
|     { | ||||
|         static THolesContainer<RawShape> empty; | ||||
|         return empty; | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static TContour<RawShape>& getHole(RawShape& sh, unsigned long idx) | ||||
|     inline TContour<RawShape>& getHole(RawShape& sh, unsigned long idx) | ||||
|     { | ||||
|         return holes(sh)[idx]; | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static const TContour<RawShape>& getHole(const RawShape& sh, | ||||
|     inline const TContour<RawShape>& getHole(const RawShape& sh, | ||||
|                                               unsigned long idx) | ||||
|     { | ||||
|         return holes(sh)[idx]; | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static size_t holeCount(const RawShape& sh) | ||||
|     inline size_t holeCount(const RawShape& sh) | ||||
|     { | ||||
|         return holes(sh).size(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static TContour<RawShape>& getContour(RawShape& sh) | ||||
|     inline TContour<RawShape>& getContour(RawShape& sh) | ||||
|     { | ||||
|         return sh; | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static const TContour<RawShape>& getContour(const RawShape& sh) | ||||
|     inline const TContour<RawShape>& getContour(const RawShape& sh) | ||||
|     { | ||||
|         return sh; | ||||
|     } | ||||
| 
 | ||||
|     // Optional, does nothing by default
 | ||||
|     template<class RawShape> | ||||
|     static void reserve(RawShape& /*sh*/,  size_t /*vertex_capacity*/) {} | ||||
|     inline void reserve(RawShape& /*sh*/,  size_t /*vertex_capacity*/) {} | ||||
| 
 | ||||
|     template<class RawShape, class...Args> | ||||
|     static void addVertex(RawShape& sh, Args...args) | ||||
|     inline void addVertex(RawShape& sh, Args...args) | ||||
|     { | ||||
|         return getContour(sh).emplace_back(std::forward<Args>(args)...); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static TVertexIterator<RawShape> begin(RawShape& sh) | ||||
|     inline typename TContour<RawShape>::iterator begin(RawShape& sh) | ||||
|     { | ||||
|         return sh.begin(); | ||||
|         return getContour(sh).begin(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static TVertexIterator<RawShape> end(RawShape& sh) | ||||
|     inline typename TContour<RawShape>::iterator end(RawShape& sh) | ||||
|     { | ||||
|         return sh.end(); | ||||
|         return getContour(sh).end(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static TVertexConstIterator<RawShape> cbegin(const RawShape& sh) | ||||
|     inline typename TContour<RawShape>::const_iterator | ||||
|     cbegin(const RawShape& sh) | ||||
|     { | ||||
|         return sh.cbegin(); | ||||
|         return getContour(sh).cbegin(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static TVertexConstIterator<RawShape> cend(const RawShape& sh) | ||||
|     inline typename TContour<RawShape>::const_iterator cend(const RawShape& sh) | ||||
|     { | ||||
|         return sh.cend(); | ||||
|         return getContour(sh).cend(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static std::string toString(const RawShape& /*sh*/) | ||||
|     inline std::string toString(const RawShape& /*sh*/) | ||||
|     { | ||||
|         return ""; | ||||
|     } | ||||
| 
 | ||||
|     template<Formats, class RawShape> | ||||
|     static std::string serialize(const RawShape& /*sh*/, double /*scale*/=1) | ||||
|     inline std::string serialize(const RawShape& /*sh*/, double /*scale*/=1) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|                       "ShapeLike::serialize() unimplemented!"); | ||||
|  | @ -481,14 +479,14 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<Formats, class RawShape> | ||||
|     static void unserialize(RawShape& /*sh*/, const std::string& /*str*/) | ||||
|     inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|                       "ShapeLike::unserialize() unimplemented!"); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static double area(const RawShape& /*sh*/) | ||||
|     inline double area(const RawShape& /*sh*/, const PolygonTag&) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|                       "ShapeLike::area() unimplemented!"); | ||||
|  | @ -496,7 +494,7 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/) | ||||
|     inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|                       "ShapeLike::intersects() unimplemented!"); | ||||
|  | @ -504,7 +502,7 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static bool isInside(const TPoint<RawShape>& /*point*/, | ||||
|     inline bool isInside(const TPoint<RawShape>& /*point*/, | ||||
|                          const RawShape& /*shape*/) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|  | @ -513,7 +511,7 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static bool isInside(const RawShape& /*shape*/, | ||||
|     inline bool isInside(const RawShape& /*shape*/, | ||||
|                          const RawShape& /*shape*/) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|  | @ -522,7 +520,7 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static bool touches( const RawShape& /*shape*/, | ||||
|     inline bool touches( const RawShape& /*shape*/, | ||||
|                          const RawShape& /*shape*/) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|  | @ -531,7 +529,7 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static bool touches( const TPoint<RawShape>& /*point*/, | ||||
|     inline bool touches( const TPoint<RawShape>& /*point*/, | ||||
|                          const RawShape& /*shape*/) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|  | @ -540,64 +538,66 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/) | ||||
|     inline _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/, | ||||
|                                               const PolygonTag&) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|                       "ShapeLike::boundingBox(shape) unimplemented!"); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static _Box<TPoint<RawShape>> boundingBox(const Shapes<RawShape>& /*sh*/) | ||||
|     template<class RawShapes> | ||||
|     inline _Box<TPoint<typename RawShapes::value_type>> | ||||
|     boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|         static_assert(always_false<RawShapes>::value, | ||||
|                       "ShapeLike::boundingBox(shapes) unimplemented!"); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static RawShape convexHull(const RawShape& /*sh*/) | ||||
|     inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|                       "ShapeLike::convexHull(shape) unimplemented!"); | ||||
|         return RawShape(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static RawShape convexHull(const Shapes<RawShape>& /*sh*/) | ||||
|     template<class RawShapes> | ||||
|     inline typename RawShapes::value_type | ||||
|     convexHull(const RawShapes& /*sh*/, const MultiPolygonTag&) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|         static_assert(always_false<RawShapes>::value, | ||||
|                       "ShapeLike::convexHull(shapes) unimplemented!"); | ||||
|         return RawShape(); | ||||
|         return typename RawShapes::value_type(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static void rotate(RawShape& /*sh*/, const Radians& /*rads*/) | ||||
|     inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|                       "ShapeLike::rotate() unimplemented!"); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape, class RawPoint> | ||||
|     static void translate(RawShape& /*sh*/, const RawPoint& /*offs*/) | ||||
|     inline void translate(RawShape& /*sh*/, const RawPoint& /*offs*/) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|                       "ShapeLike::translate() unimplemented!"); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/) | ||||
|     inline void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/) | ||||
|     { | ||||
|         static_assert(always_false<RawShape>::value, | ||||
|                       "ShapeLike::offset() unimplemented!"); | ||||
|         dout() << "The current geometry backend does not support offsetting!\n"; | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static std::pair<bool, std::string> isValid(const RawShape& /*sh*/) | ||||
|     inline std::pair<bool, std::string> isValid(const RawShape& /*sh*/) | ||||
|     { | ||||
|         return {false, "ShapeLike::isValid() unimplemented!"}; | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static inline bool isConvex(const TContour<RawShape>& sh) | ||||
|     inline bool isConvex(const TContour<RawShape>& sh) | ||||
|     { | ||||
|         using Vertex = TPoint<RawShape>; | ||||
|         auto first = sh.begin(); | ||||
|  | @ -633,43 +633,55 @@ struct ShapeLike { | |||
|     // No need to implement these
 | ||||
|     // *************************************************************************
 | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static inline _Box<TPoint<RawShape>> boundingBox( | ||||
|             const _Box<TPoint<RawShape>>& box) | ||||
|     template<class Box> | ||||
|     inline Box boundingBox(const Box& box, const BoxTag& ) | ||||
|     { | ||||
|         return box; | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static inline _Box<TPoint<RawShape>> boundingBox( | ||||
|             const _Circle<TPoint<RawShape>>& circ) | ||||
|     template<class Circle> | ||||
|     inline _Box<typename Circle::PointType> boundingBox( | ||||
|             const Circle& circ, const CircleTag&) | ||||
|     { | ||||
|         using Coord = TCoord<TPoint<RawShape>>; | ||||
|         TPoint<RawShape> pmin = { | ||||
|         using Point = typename Circle::PointType; | ||||
|         using Coord = TCoord<Point>; | ||||
|         Point pmin = { | ||||
|             static_cast<Coord>(getX(circ.center()) - circ.radius()), | ||||
|             static_cast<Coord>(getY(circ.center()) - circ.radius()) }; | ||||
| 
 | ||||
|         TPoint<RawShape> pmax = { | ||||
|         Point pmax = { | ||||
|             static_cast<Coord>(getX(circ.center()) + circ.radius()), | ||||
|             static_cast<Coord>(getY(circ.center()) + circ.radius()) }; | ||||
| 
 | ||||
|         return {pmin, pmax}; | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static inline double area(const _Box<TPoint<RawShape>>& box) | ||||
|     template<class S> // Dispatch function
 | ||||
|     inline _Box<TPoint<S>> boundingBox(const S& sh) | ||||
|     { | ||||
|         return static_cast<double>(box.width() * box.height()); | ||||
|         return boundingBox(sh, Tag<S>() ); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static inline double area(const _Circle<TPoint<RawShape>>& circ) | ||||
|     template<class Box> | ||||
|     inline double area(const Box& box, const BoxTag& ) | ||||
|     { | ||||
|         return box.area(); | ||||
|     } | ||||
| 
 | ||||
|     template<class Circle> | ||||
|     inline double area(const Circle& circ, const CircleTag& ) | ||||
|     { | ||||
|         return circ.area(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> // Dispatching function
 | ||||
|     inline double area(const RawShape& sh) | ||||
|     { | ||||
|         return area(sh, Tag<RawShape>()); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static inline double area(const Shapes<RawShape>& shapes) | ||||
|     inline double area(const Shapes<RawShape>& shapes) | ||||
|     { | ||||
|         return std::accumulate(shapes.begin(), shapes.end(), 0.0, | ||||
|                         [](double a, const RawShape& b) { | ||||
|  | @ -678,14 +690,21 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static bool isInside(const TPoint<RawShape>& point, | ||||
|                          const _Circle<TPoint<RawShape>>& circ) | ||||
|     inline auto convexHull(const RawShape& sh) | ||||
|         -> decltype(convexHull(sh, Tag<RawShape>())) // TODO: C++14 could deduce
 | ||||
|     { | ||||
|         return PointLike::distance(point, circ.center()) < circ.radius(); | ||||
|         return convexHull(sh, Tag<RawShape>()); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static bool isInside(const TPoint<RawShape>& point, | ||||
|     inline bool isInside(const TPoint<RawShape>& point, | ||||
|                          const _Circle<TPoint<RawShape>>& circ) | ||||
|     { | ||||
|         return pointlike::distance(point, circ.center()) < circ.radius(); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     inline bool isInside(const TPoint<RawShape>& point, | ||||
|                          const _Box<TPoint<RawShape>>& box) | ||||
|     { | ||||
|         auto px = getX(point); | ||||
|  | @ -699,7 +718,7 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static bool isInside(const RawShape& sh, | ||||
|     inline bool isInside(const RawShape& sh, | ||||
|                          const _Circle<TPoint<RawShape>>& circ) | ||||
|     { | ||||
|         return std::all_of(cbegin(sh), cend(sh), | ||||
|  | @ -709,7 +728,7 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static bool isInside(const _Box<TPoint<RawShape>>& box, | ||||
|     inline bool isInside(const _Box<TPoint<RawShape>>& box, | ||||
|                          const _Circle<TPoint<RawShape>>& circ) | ||||
|     { | ||||
|         return isInside<RawShape>(box.minCorner(), circ) && | ||||
|  | @ -717,7 +736,7 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static bool isInside(const _Box<TPoint<RawShape>>& ibb, | ||||
|     inline bool isInside(const _Box<TPoint<RawShape>>& ibb, | ||||
|                          const _Box<TPoint<RawShape>>& box) | ||||
|     { | ||||
|         auto iminX = getX(ibb.minCorner()); | ||||
|  | @ -734,31 +753,31 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape> // Potential O(1) implementation may exist
 | ||||
|     static inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx) | ||||
|     inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx) | ||||
|     { | ||||
|         return *(begin(sh) + idx); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> // Potential O(1) implementation may exist
 | ||||
|     static inline const TPoint<RawShape>& vertex(const RawShape& sh, | ||||
|     inline const TPoint<RawShape>& vertex(const RawShape& sh, | ||||
|                                           unsigned long idx) | ||||
|     { | ||||
|         return *(cbegin(sh) + idx); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape> | ||||
|     static inline size_t contourVertexCount(const RawShape& sh) | ||||
|     inline size_t contourVertexCount(const RawShape& sh) | ||||
|     { | ||||
|         return cend(sh) - cbegin(sh); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape, class Fn> | ||||
|     static inline void foreachContourVertex(RawShape& sh, Fn fn) { | ||||
|     inline void foreachContourVertex(RawShape& sh, Fn fn) { | ||||
|         for(auto it = begin(sh); it != end(sh); ++it) fn(*it); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape, class Fn> | ||||
|     static inline void foreachHoleVertex(RawShape& sh, Fn fn) { | ||||
|     inline void foreachHoleVertex(RawShape& sh, Fn fn) { | ||||
|         for(int i = 0; i < holeCount(sh); ++i) { | ||||
|             auto& h = getHole(sh, i); | ||||
|             for(auto it = begin(h); it != end(h); ++it) fn(*it); | ||||
|  | @ -766,12 +785,12 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape, class Fn> | ||||
|     static inline void foreachContourVertex(const RawShape& sh, Fn fn) { | ||||
|     inline void foreachContourVertex(const RawShape& sh, Fn fn) { | ||||
|         for(auto it = cbegin(sh); it != cend(sh); ++it) fn(*it); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape, class Fn> | ||||
|     static inline void foreachHoleVertex(const RawShape& sh, Fn fn) { | ||||
|     inline void foreachHoleVertex(const RawShape& sh, Fn fn) { | ||||
|         for(int i = 0; i < holeCount(sh); ++i) { | ||||
|             auto& h = getHole(sh, i); | ||||
|             for(auto it = cbegin(h); it != cend(h); ++it) fn(*it); | ||||
|  | @ -779,18 +798,27 @@ struct ShapeLike { | |||
|     } | ||||
| 
 | ||||
|     template<class RawShape, class Fn> | ||||
|     static inline void foreachVertex(RawShape& sh, Fn fn) { | ||||
|     inline void foreachVertex(RawShape& sh, Fn fn) { | ||||
|         foreachContourVertex(sh, fn); | ||||
|         foreachHoleVertex(sh, fn); | ||||
|     } | ||||
| 
 | ||||
|     template<class RawShape, class Fn> | ||||
|     static inline void foreachVertex(const RawShape& sh, Fn fn) { | ||||
|     inline void foreachVertex(const RawShape& sh, Fn fn) { | ||||
|         foreachContourVertex(sh, fn); | ||||
|         foreachHoleVertex(sh, fn); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| }; | ||||
| #define DECLARE_MAIN_TYPES(T)        \ | ||||
|     using Polygon = T;               \ | ||||
|     using Point   = TPoint<T>;       \ | ||||
|     using Coord   = TCoord<Point>;   \ | ||||
|     using Contour = TContour<T>;     \ | ||||
|     using Box     = _Box<Point>;     \ | ||||
|     using Circle  = _Circle<Point>;  \ | ||||
|     using Segment = _Segment<Point>; \ | ||||
|     using Polygons = TMultiShape<T> | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,6 +9,27 @@ | |||
| 
 | ||||
| namespace libnest2d { | ||||
| 
 | ||||
| namespace __nfp { | ||||
| // Do not specialize this...
 | ||||
| template<class RawShape> | ||||
| inline bool _vsort(const TPoint<RawShape>& v1, const TPoint<RawShape>& v2) | ||||
| { | ||||
|     using Coord = TCoord<TPoint<RawShape>>; | ||||
|     Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2); | ||||
|     auto diff = y1 - y2; | ||||
|     if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon()) | ||||
|         return x1 < x2; | ||||
| 
 | ||||
|     return diff < 0; | ||||
| } | ||||
| } | ||||
| 
 | ||||
| /// A collection of static methods for handling the no fit polygon creation.
 | ||||
| namespace nfp { | ||||
| 
 | ||||
| //namespace sl = shapelike;
 | ||||
| //namespace pl = pointlike;
 | ||||
| 
 | ||||
| /// The complexity level of a polygon that an NFP implementation can handle.
 | ||||
| enum class NfpLevel: unsigned { | ||||
|     CONVEX_ONLY, | ||||
|  | @ -18,12 +39,17 @@ enum class NfpLevel: unsigned { | |||
|     BOTH_CONCAVE_WITH_HOLES | ||||
| }; | ||||
| 
 | ||||
| /// A collection of static methods for handling the no fit polygon creation.
 | ||||
| struct Nfp { | ||||
| template<class RawShape> | ||||
| using NfpResult = std::pair<RawShape, TPoint<RawShape>>; | ||||
| 
 | ||||
| template<class RawShape> struct MaxNfpLevel { | ||||
|     static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| // Shorthand for a pile of polygons
 | ||||
| template<class RawShape> | ||||
| using Shapes = typename ShapeLike::Shapes<RawShape>; | ||||
| using Shapes = TMultiShape<RawShape>; | ||||
| 
 | ||||
| /**
 | ||||
|  * Merge a bunch of polygons with the specified additional polygon. | ||||
|  | @ -36,10 +62,10 @@ using Shapes = typename ShapeLike::Shapes<RawShape>; | |||
|  * mostly it will be a set containing only one big polygon but if the input | ||||
|  * polygons are disjuct than the resulting set will contain more polygons. | ||||
|  */ | ||||
| template<class RawShape> | ||||
| static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/) | ||||
| template<class RawShapes> | ||||
| inline RawShapes merge(const RawShapes& /*shc*/) | ||||
| { | ||||
|     static_assert(always_false<RawShape>::value, | ||||
|     static_assert(always_false<RawShapes>::value, | ||||
|                   "Nfp::merge(shapes, shape) unimplemented!"); | ||||
| } | ||||
| 
 | ||||
|  | @ -55,12 +81,44 @@ static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/) | |||
|  * polygons are disjuct than the resulting set will contain more polygons. | ||||
|  */ | ||||
| template<class RawShape> | ||||
| static Shapes<RawShape> merge(const Shapes<RawShape>& shc, | ||||
|                               const RawShape& sh) | ||||
| inline TMultiShape<RawShape> merge(const TMultiShape<RawShape>& shc, | ||||
|                                    const RawShape& sh) | ||||
| { | ||||
|     auto m = merge(shc); | ||||
|     auto m = nfp::merge(shc); | ||||
|     m.push_back(sh); | ||||
|     return merge(m); | ||||
|     return nfp::merge(m); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the vertex of the polygon that is at the lowest values (bottom) in the Y | ||||
|  * axis and if there are more than one vertices on the same Y coordinate than | ||||
|  * the result will be the leftmost (with the highest X coordinate). | ||||
|  */ | ||||
| template<class RawShape> | ||||
| inline TPoint<RawShape> leftmostDownVertex(const RawShape& sh) | ||||
| { | ||||
| 
 | ||||
|     // find min x and min y vertex
 | ||||
|     auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh), | ||||
|                                __nfp::_vsort<RawShape>); | ||||
| 
 | ||||
|     return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the vertex of the polygon that is at the highest values (top) in the Y | ||||
|  * axis and if there are more than one vertices on the same Y coordinate than | ||||
|  * the result will be the rightmost (with the lowest X coordinate). | ||||
|  */ | ||||
| template<class RawShape> | ||||
| TPoint<RawShape> rightmostUpVertex(const RawShape& sh) | ||||
| { | ||||
| 
 | ||||
|     // find max x and max y vertex
 | ||||
|     auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh), | ||||
|                                __nfp::_vsort<RawShape>); | ||||
| 
 | ||||
|     return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -71,55 +129,11 @@ static Shapes<RawShape> merge(const Shapes<RawShape>& shc, | |||
|  * reference will be always the same for the same polygon. | ||||
|  */ | ||||
| template<class RawShape> | ||||
| inline static TPoint<RawShape> referenceVertex(const RawShape& sh) | ||||
| inline TPoint<RawShape> referenceVertex(const RawShape& sh) | ||||
| { | ||||
|     return rightmostUpVertex(sh); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the vertex of the polygon that is at the lowest values (bottom) in the Y | ||||
|  * axis and if there are more than one vertices on the same Y coordinate than | ||||
|  * the result will be the leftmost (with the highest X coordinate). | ||||
|  */ | ||||
| template<class RawShape> | ||||
| static TPoint<RawShape> leftmostDownVertex(const RawShape& sh) | ||||
| { | ||||
| 
 | ||||
|     // find min x and min y vertex
 | ||||
|     auto it = std::min_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh), | ||||
|                                _vsort<RawShape>); | ||||
| 
 | ||||
|     return *it; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the vertex of the polygon that is at the highest values (top) in the Y | ||||
|  * axis and if there are more than one vertices on the same Y coordinate than | ||||
|  * the result will be the rightmost (with the lowest X coordinate). | ||||
|  */ | ||||
| template<class RawShape> | ||||
| static TPoint<RawShape> rightmostUpVertex(const RawShape& sh) | ||||
| { | ||||
| 
 | ||||
|     // find max x and max y vertex
 | ||||
|     auto it = std::max_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh), | ||||
|                                _vsort<RawShape>); | ||||
| 
 | ||||
|     return *it; | ||||
| } | ||||
| 
 | ||||
| template<class RawShape> | ||||
| using NfpResult = std::pair<RawShape, TPoint<RawShape>>; | ||||
| 
 | ||||
| /// Helper function to get the NFP
 | ||||
| template<NfpLevel nfptype, class RawShape> | ||||
| static NfpResult<RawShape> noFitPolygon(const RawShape& sh, | ||||
|                                         const RawShape& other) | ||||
| { | ||||
|     NfpImpl<RawShape, nfptype> nfp; | ||||
|     return nfp(sh, other); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * The "trivial" Cuninghame-Green implementation of NFP for convex polygons. | ||||
|  * | ||||
|  | @ -139,11 +153,11 @@ static NfpResult<RawShape> noFitPolygon(const RawShape& sh, | |||
|  * | ||||
|  */ | ||||
| template<class RawShape> | ||||
| static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh, | ||||
| inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh, | ||||
|                                          const RawShape& other) | ||||
| { | ||||
|     using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>; | ||||
|     using sl = ShapeLike; | ||||
|     namespace sl = shapelike; | ||||
| 
 | ||||
|     RawShape rsh;   // Final nfp placeholder
 | ||||
|     Vertex top_nfp; | ||||
|  | @ -187,7 +201,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh, | |||
|     sl::addVertex(rsh, edgelist.front().second()); | ||||
| 
 | ||||
|     // Sorting function for the nfp reference vertex search
 | ||||
|     auto& cmp = _vsort<RawShape>; | ||||
|     auto& cmp = __nfp::_vsort<RawShape>; | ||||
| 
 | ||||
|     // the reference (rightmost top) vertex so far
 | ||||
|     top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp ); | ||||
|  | @ -214,7 +228,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh, | |||
| } | ||||
| 
 | ||||
| template<class RawShape> | ||||
| static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary, | ||||
| NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary, | ||||
|                                            const RawShape& cother) | ||||
| { | ||||
| 
 | ||||
|  | @ -233,7 +247,7 @@ static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary, | |||
|     using Vertex = TPoint<RawShape>; | ||||
|     using Coord = TCoord<Vertex>; | ||||
|     using Edge = _Segment<Vertex>; | ||||
|     using sl = ShapeLike; | ||||
|     namespace sl = shapelike; | ||||
|     using std::signbit; | ||||
|     using std::sort; | ||||
|     using std::vector; | ||||
|  | @ -528,27 +542,16 @@ struct NfpImpl { | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| template<class RawShape> struct MaxNfpLevel { | ||||
|     static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY; | ||||
| }; | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
| // Do not specialize this...
 | ||||
| template<class RawShape> | ||||
| static inline bool _vsort(const TPoint<RawShape>& v1, | ||||
|                           const TPoint<RawShape>& v2) | ||||
| /// Helper function to get the NFP
 | ||||
| template<NfpLevel nfptype, class RawShape> | ||||
| inline NfpResult<RawShape> noFitPolygon(const RawShape& sh, | ||||
|                                         const RawShape& other) | ||||
| { | ||||
|     using Coord = TCoord<TPoint<RawShape>>; | ||||
|     Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2); | ||||
|     auto diff = y1 - y2; | ||||
|     if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon()) | ||||
|         return x1 < x2; | ||||
| 
 | ||||
|     return diff < 0; | ||||
|     NfpImpl<RawShape, nfptype> nfps; | ||||
|     return nfps(sh, other); | ||||
| } | ||||
| 
 | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,10 +9,12 @@ | |||
| #include <functional> | ||||
| 
 | ||||
| #include "geometry_traits.hpp" | ||||
| #include "optimizer.hpp" | ||||
| 
 | ||||
| namespace libnest2d { | ||||
| 
 | ||||
| namespace sl = shapelike; | ||||
| namespace pl = pointlike; | ||||
| 
 | ||||
| /**
 | ||||
|  * \brief An item to be placed on a bin. | ||||
|  * | ||||
|  | @ -28,7 +30,8 @@ class _Item { | |||
|     using Coord = TCoord<TPoint<RawShape>>; | ||||
|     using Vertex = TPoint<RawShape>; | ||||
|     using Box = _Box<Vertex>; | ||||
|     using sl = ShapeLike; | ||||
| 
 | ||||
|     using VertexConstIterator = typename TContour<RawShape>::const_iterator; | ||||
| 
 | ||||
|     // The original shape that gets encapsulated.
 | ||||
|     RawShape sh_; | ||||
|  | @ -38,7 +41,7 @@ class _Item { | |||
|     Radians rotation_; | ||||
|     Coord offset_distance_; | ||||
| 
 | ||||
|     // Info about whether the tranformations will have to take place
 | ||||
|     // Info about whether the transformations will have to take place
 | ||||
|     // This is needed because if floating point is used, it is hard to say
 | ||||
|     // that a zero angle is not a rotation because of testing for equality.
 | ||||
|     bool has_rotation_ = false, has_translation_ = false, has_offset_ = false; | ||||
|  | @ -58,12 +61,12 @@ class _Item { | |||
|     }; | ||||
| 
 | ||||
|     mutable Convexity convexity_ = Convexity::UNCHECKED; | ||||
|     mutable TVertexConstIterator<RawShape> rmt_;    // rightmost top vertex
 | ||||
|     mutable TVertexConstIterator<RawShape> lmb_;    // leftmost bottom vertex
 | ||||
|     mutable VertexConstIterator rmt_;    // rightmost top vertex
 | ||||
|     mutable VertexConstIterator lmb_;    // leftmost bottom vertex
 | ||||
|     mutable bool rmt_valid_ = false, lmb_valid_ = false; | ||||
|     mutable struct BBCache { | ||||
|         Box bb; bool valid; Vertex tr; | ||||
|         BBCache(): valid(false), tr(0, 0) {} | ||||
|         Box bb; bool valid; | ||||
|         BBCache(): valid(false) {} | ||||
|     } bb_cache_; | ||||
| 
 | ||||
| public: | ||||
|  | @ -80,7 +83,7 @@ public: | |||
|      * supports. Giving out a non const iterator would make it impossible to | ||||
|      * perform correct cache invalidation. | ||||
|      */ | ||||
|     using Iterator = TVertexConstIterator<RawShape>; | ||||
|     using Iterator = VertexConstIterator; | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Get the orientation of the polygon. | ||||
|  | @ -109,7 +112,7 @@ public: | |||
|     explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {} | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Create an item from an initilizer list. | ||||
|      * @brief Create an item from an initializer list. | ||||
|      * @param il The initializer list of vertices. | ||||
|      */ | ||||
|     inline _Item(const std::initializer_list< Vertex >& il): | ||||
|  | @ -159,7 +162,7 @@ public: | |||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Get a copy of an outer vertex whithin the carried shape. | ||||
|      * @brief Get a copy of an outer vertex within the carried shape. | ||||
|      * | ||||
|      * Note that the vertex considered here is taken from the original shape | ||||
|      * that this item is constructed from. This means that no transformation is | ||||
|  | @ -244,7 +247,7 @@ public: | |||
|      * @param p | ||||
|      * @return | ||||
|      */ | ||||
|     inline bool isPointInside(const Vertex& p) const | ||||
|     inline bool isInside(const Vertex& p) const | ||||
|     { | ||||
|         return sl::isInside(p, transformedShape()); | ||||
|     } | ||||
|  | @ -307,7 +310,7 @@ public: | |||
|     { | ||||
|         if(translation_ != tr) { | ||||
|             translation_ = tr; has_translation_ = true; tr_cache_valid_ = false; | ||||
|             bb_cache_.valid = false; | ||||
|             //bb_cache_.valid = false;
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -342,13 +345,19 @@ public: | |||
| 
 | ||||
|     inline Box boundingBox() const { | ||||
|         if(!bb_cache_.valid) { | ||||
|             bb_cache_.bb = sl::boundingBox(transformedShape()); | ||||
|             bb_cache_.tr = {0, 0}; | ||||
|             if(!has_rotation_) | ||||
|                 bb_cache_.bb = sl::boundingBox(offsettedShape()); | ||||
|             else { | ||||
|                 // TODO make sure this works
 | ||||
|                 auto rotsh = offsettedShape(); | ||||
|                 sl::rotate(rotsh, rotation_); | ||||
|                 bb_cache_.bb = sl::boundingBox(rotsh); | ||||
|             } | ||||
|             bb_cache_.valid = true; | ||||
|         } | ||||
| 
 | ||||
|         auto &bb = bb_cache_.bb; auto &tr = bb_cache_.tr; | ||||
|         return {bb.minCorner() + tr, bb.maxCorner() + tr}; | ||||
|         auto &bb = bb_cache_.bb; auto &tr = translation_; | ||||
|         return {bb.minCorner() + tr, bb.maxCorner() + tr }; | ||||
|     } | ||||
| 
 | ||||
|     inline Vertex referenceVertex() const { | ||||
|  | @ -438,7 +447,7 @@ public: | |||
|     inline _Rectangle(Unit width, Unit height, | ||||
|                       // disable this ctor if o != CLOCKWISE
 | ||||
|                       enable_if_t< o == TO::CLOCKWISE, int> = 0 ): | ||||
|         _Item<RawShape>( ShapeLike::create<RawShape>( { | ||||
|         _Item<RawShape>( sl::create<RawShape>( { | ||||
|                                                         {0, 0}, | ||||
|                                                         {0, height}, | ||||
|                                                         {width, height}, | ||||
|  | @ -452,7 +461,7 @@ public: | |||
|     inline _Rectangle(Unit width, Unit height, | ||||
|                       // disable this ctor if o != COUNTER_CLOCKWISE
 | ||||
|                       enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ): | ||||
|         _Item<RawShape>( ShapeLike::create<RawShape>( { | ||||
|         _Item<RawShape>( sl::create<RawShape>( { | ||||
|                                                         {0, 0}, | ||||
|                                                         {width, 0}, | ||||
|                                                         {width, height}, | ||||
|  | @ -473,18 +482,38 @@ public: | |||
| 
 | ||||
| template<class RawShape> | ||||
| inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const { | ||||
|     return ShapeLike::isInside<RawShape>(boundingBox(), box); | ||||
|     return sl::isInside<RawShape>(boundingBox(), box); | ||||
| } | ||||
| 
 | ||||
| template<class RawShape> inline bool | ||||
| _Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const { | ||||
|     return ShapeLike::isInside<RawShape>(transformedShape(), circ); | ||||
|     return sl::isInside<RawShape>(transformedShape(), circ); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template<class I> using _ItemRef = std::reference_wrapper<I>; | ||||
| template<class I> using _ItemGroup = std::vector<_ItemRef<I>>; | ||||
| 
 | ||||
| template<class Iterator> | ||||
| struct ConstItemRange { | ||||
|     Iterator from; | ||||
|     Iterator to; | ||||
|     bool valid = false; | ||||
| 
 | ||||
|     ConstItemRange() = default; | ||||
|     ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {} | ||||
| }; | ||||
| 
 | ||||
| template<class Container> | ||||
| inline ConstItemRange<typename Container::const_iterator> | ||||
| rem(typename Container::const_iterator it, const Container& cont) { | ||||
|     return {std::next(it), cont.end()}; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * \brief A wrapper interface (trait) class for any placement strategy provider. | ||||
|  * | ||||
|  * If a client want's to use its own placement algorithm, all it has to do is to | ||||
|  * If a client wants to use its own placement algorithm, all it has to do is to | ||||
|  * specialize this class template and define all the ten methods it has. It can | ||||
|  * use the strategies::PlacerBoilerplace class for creating a new placement | ||||
|  * strategy where only the constructor and the trypack method has to be provided | ||||
|  | @ -515,8 +544,9 @@ public: | |||
|      */ | ||||
|     using PackResult = typename PlacementStrategy::PackResult; | ||||
| 
 | ||||
|     using ItemRef = std::reference_wrapper<Item>; | ||||
|     using ItemGroup = std::vector<ItemRef>; | ||||
|     using ItemRef = _ItemRef<Item>; | ||||
|     using ItemGroup = _ItemGroup<Item>; | ||||
|     using DefaultIterator = typename ItemGroup::const_iterator; | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Constructor taking the bin and an optional configuration. | ||||
|  | @ -536,29 +566,32 @@ public: | |||
|      * Note that it depends on the particular placer implementation how it | ||||
|      * reacts to config changes in the middle of a calculation. | ||||
|      * | ||||
|      * @param config The configuration object defined by the placement startegy. | ||||
|      * @param config The configuration object defined by the placement strategy. | ||||
|      */ | ||||
|     inline void configure(const Config& config) { impl_.configure(config); } | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief A method that tries to pack an item and returns an object | ||||
|      * describing the pack result. | ||||
|      * Try to pack an item with a result object that contains the packing | ||||
|      * information for later accepting it. | ||||
|      * | ||||
|      * The result can be casted to bool and used as an argument to the accept | ||||
|      * method to accept a succesfully packed item. This way the next packing | ||||
|      * will consider the accepted item as well. The PackResult should carry the | ||||
|      * transformation info so that if the tried item is later modified or tried | ||||
|      * multiple times, the result object should set it to the originally | ||||
|      * determied position. An implementation can be found in the | ||||
|      * strategies::PlacerBoilerplate::PackResult class. | ||||
|      * | ||||
|      * @param item Ithe item to be packed. | ||||
|      * @return The PackResult object that can be implicitly casted to bool. | ||||
|      * \param item_store A container of items that are intended to be packed | ||||
|      * later. Can be used by the placer to switch tactics. When it's knows that | ||||
|      * many items will come a greedy strategy may not be the best. | ||||
|      * \param from The iterator to the item from which the packing should start, | ||||
|      * including the pointed item | ||||
|      * \param count How many items should be packed. If the value is 1, than | ||||
|      * just the item pointed to by "from" argument should be packed. | ||||
|      */ | ||||
|     inline PackResult trypack(Item& item) { return impl_.trypack(item); } | ||||
|     template<class Iter = DefaultIterator> | ||||
|     inline PackResult trypack( | ||||
|             Item& item, | ||||
|             const ConstItemRange<Iter>& remaining = ConstItemRange<Iter>()) | ||||
|     { | ||||
|         return impl_.trypack(item, remaining); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief A method to accept a previously tried item. | ||||
|      * @brief A method to accept a previously tried item (or items). | ||||
|      * | ||||
|      * If the pack result is a failure the method should ignore it. | ||||
|      * @param r The result of a previous trypack call. | ||||
|  | @ -566,19 +599,25 @@ public: | |||
|     inline void accept(PackResult& r) { impl_.accept(r); } | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief pack Try to pack an item and immediately accept it on success. | ||||
|      * @brief pack Try to pack and immediately accept it on success. | ||||
|      * | ||||
|      * A default implementation would be to call | ||||
|      * { auto&& r = trypack(item); accept(r); return r; } but we should let the | ||||
|      * { auto&& r = trypack(...); accept(r); return r; } but we should let the | ||||
|      * implementor of the placement strategy to harvest any optimizations from | ||||
|      * the absence of an intermadiate step. The above version can still be used | ||||
|      * the absence of an intermediate step. The above version can still be used | ||||
|      * in the implementation. | ||||
|      * | ||||
|      * @param item The item to pack. | ||||
|      * @return Returns true if the item was packed or false if it could not be | ||||
|      * packed. | ||||
|      */ | ||||
|     inline bool pack(Item& item) { return impl_.pack(item); } | ||||
|     template<class Range = ConstItemRange<DefaultIterator>> | ||||
|     inline bool pack( | ||||
|             Item& item, | ||||
|             const Range& remaining = Range()) | ||||
|     { | ||||
|         return impl_.pack(item, remaining); | ||||
|     } | ||||
| 
 | ||||
|     /// Unpack the last element (remove it from the list of packed items).
 | ||||
|     inline void unpackLast() { impl_.unpackLast(); } | ||||
|  | @ -597,13 +636,6 @@ public: | |||
| 
 | ||||
|     inline double filledArea() const { return impl_.filledArea(); } | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
|     inline auto getDebugItems() -> decltype(impl_.debug_items_)& | ||||
|     { | ||||
|         return impl_.debug_items_; | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| // The progress function will be called with the number of placed items
 | ||||
|  | @ -628,15 +660,15 @@ public: | |||
|      * Note that it depends on the particular placer implementation how it | ||||
|      * reacts to config changes in the middle of a calculation. | ||||
|      * | ||||
|      * @param config The configuration object defined by the selection startegy. | ||||
|      * @param config The configuration object defined by the selection strategy. | ||||
|      */ | ||||
|     inline void configure(const Config& config) { | ||||
|         impl_.configure(config); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief A function callback which should be called whenewer an item or | ||||
|      * a group of items where succesfully packed. | ||||
|      * @brief A function callback which should be called whenever an item or | ||||
|      * a group of items where successfully packed. | ||||
|      * @param fn A function callback object taking one unsigned integer as the | ||||
|      * number of the remaining items to pack. | ||||
|      */ | ||||
|  | @ -649,7 +681,7 @@ public: | |||
|      * placer compatible with the PlacementStrategyLike interface. | ||||
|      * | ||||
|      * \param first, last The first and last iterator if the input sequence. It | ||||
|      * can be only an iterator of a type converitible to Item. | ||||
|      * can be only an iterator of a type convertible to Item. | ||||
|      * \param bin. The shape of the bin. It has to be supported by the placement | ||||
|      * strategy. | ||||
|      * \param An optional config object for the placer. | ||||
|  | @ -681,7 +713,7 @@ public: | |||
|     /**
 | ||||
|      * @brief Get the items for a particular bin. | ||||
|      * @param binIndex The index of the requested bin. | ||||
|      * @return Returns a list of allitems packed into the requested bin. | ||||
|      * @return Returns a list of all items packed into the requested bin. | ||||
|      */ | ||||
|     inline ItemGroup itemsForBin(size_t binIndex) { | ||||
|         return impl_.itemsForBin(binIndex); | ||||
|  | @ -723,15 +755,14 @@ using _IndexedPackGroup = std::vector< | |||
|                           >; | ||||
| 
 | ||||
| /**
 | ||||
|  * The Arranger is the frontend class for the binpack2d library. It takes the | ||||
|  * The Arranger is the front-end class for the libnest2d library. It takes the | ||||
|  * input items and outputs the items with the proper transformations to be | ||||
|  * inside the provided bin. | ||||
|  */ | ||||
| template<class PlacementStrategy, class SelectionStrategy > | ||||
| class Arranger { | ||||
| class Nester { | ||||
|     using TSel = SelectionStrategyLike<SelectionStrategy>; | ||||
|     TSel selector_; | ||||
|     bool use_min_bb_rotation_ = false; | ||||
| public: | ||||
|     using Item = typename PlacementStrategy::Item; | ||||
|     using ItemRef = std::reference_wrapper<Item>; | ||||
|  | @ -769,7 +800,7 @@ public: | |||
|     template<class TBinType = BinType, | ||||
|              class PConf = PlacementConfig, | ||||
|              class SConf = SelectionConfig> | ||||
|     Arranger( TBinType&& bin, | ||||
|     Nester( TBinType&& bin, | ||||
|               Unit min_obj_distance = 0, | ||||
|               PConf&& pconfig = PConf(), | ||||
|               SConf&& sconfig = SConf()): | ||||
|  | @ -802,9 +833,9 @@ public: | |||
|      * the selection algorithm. | ||||
|      */ | ||||
|     template<class TIterator> | ||||
|     inline PackGroup arrange(TIterator from, TIterator to) | ||||
|     inline PackGroup execute(TIterator from, TIterator to) | ||||
|     { | ||||
|         return _arrange(from, to); | ||||
|         return _execute(from, to); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|  | @ -815,20 +846,20 @@ public: | |||
|      * input sequence size. | ||||
|      */ | ||||
|     template<class TIterator> | ||||
|     inline IndexedPackGroup arrangeIndexed(TIterator from, TIterator to) | ||||
|     inline IndexedPackGroup executeIndexed(TIterator from, TIterator to) | ||||
|     { | ||||
|         return _arrangeIndexed(from, to); | ||||
|         return _executeIndexed(from, to); | ||||
|     } | ||||
| 
 | ||||
|     /// Shorthand to normal arrange method.
 | ||||
|     template<class TIterator> | ||||
|     inline PackGroup operator() (TIterator from, TIterator to) | ||||
|     { | ||||
|         return _arrange(from, to); | ||||
|         return _execute(from, to); | ||||
|     } | ||||
| 
 | ||||
|     /// Set a progress indicatior function object for the selector.
 | ||||
|     inline Arranger& progressIndicator(ProgressFunction func) | ||||
|     /// Set a progress indicator function object for the selector.
 | ||||
|     inline Nester& progressIndicator(ProgressFunction func) | ||||
|     { | ||||
|         selector_.progressIndicator(func); return *this; | ||||
|     } | ||||
|  | @ -842,24 +873,20 @@ public: | |||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     inline Arranger& useMinimumBoundigBoxRotation(bool s = true) { | ||||
|         use_min_bb_rotation_ = s; return *this; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     template<class TIterator, | ||||
|              class IT = remove_cvref_t<typename TIterator::value_type>, | ||||
| 
 | ||||
|              // This funtion will be used only if the iterators are pointing to
 | ||||
|              // a type compatible with the binpack2d::_Item template.
 | ||||
|              // This function will be used only if the iterators are pointing to
 | ||||
|              // a type compatible with the libnets2d::_Item template.
 | ||||
|              // This way we can use references to input elements as they will
 | ||||
|              // have to exist for the lifetime of this call.
 | ||||
|              class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT> | ||||
|              > | ||||
|     inline PackGroup _arrange(TIterator from, TIterator to, bool = false) | ||||
|     inline PackGroup _execute(TIterator from, TIterator to, bool = false) | ||||
|     { | ||||
|         __arrange(from, to); | ||||
|         __execute(from, to); | ||||
|         return lastResult(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -867,28 +894,28 @@ private: | |||
|              class IT = remove_cvref_t<typename TIterator::value_type>, | ||||
|              class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT> | ||||
|              > | ||||
|     inline PackGroup _arrange(TIterator from, TIterator to, int = false) | ||||
|     inline PackGroup _execute(TIterator from, TIterator to, int = false) | ||||
|     { | ||||
|         item_cache_ = {from, to}; | ||||
| 
 | ||||
|         __arrange(item_cache_.begin(), item_cache_.end()); | ||||
|         __execute(item_cache_.begin(), item_cache_.end()); | ||||
|         return lastResult(); | ||||
|     } | ||||
| 
 | ||||
|     template<class TIterator, | ||||
|              class IT = remove_cvref_t<typename TIterator::value_type>, | ||||
| 
 | ||||
|              // This funtion will be used only if the iterators are pointing to
 | ||||
|              // a type compatible with the binpack2d::_Item template.
 | ||||
|              // This function will be used only if the iterators are pointing to
 | ||||
|              // a type compatible with the libnest2d::_Item template.
 | ||||
|              // This way we can use references to input elements as they will
 | ||||
|              // have to exist for the lifetime of this call.
 | ||||
|              class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT> | ||||
|              > | ||||
|     inline IndexedPackGroup _arrangeIndexed(TIterator from, | ||||
|     inline IndexedPackGroup _executeIndexed(TIterator from, | ||||
|                                             TIterator to, | ||||
|                                             bool = false) | ||||
|     { | ||||
|         __arrange(from, to); | ||||
|         __execute(from, to); | ||||
|         return createIndexedPackGroup(from, to, selector_); | ||||
|     } | ||||
| 
 | ||||
|  | @ -896,12 +923,12 @@ private: | |||
|              class IT = remove_cvref_t<typename TIterator::value_type>, | ||||
|              class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT> | ||||
|              > | ||||
|     inline IndexedPackGroup _arrangeIndexed(TIterator from, | ||||
|     inline IndexedPackGroup _executeIndexed(TIterator from, | ||||
|                                             TIterator to, | ||||
|                                             int = false) | ||||
|     { | ||||
|         item_cache_ = {from, to}; | ||||
|         __arrange(item_cache_.begin(), item_cache_.end()); | ||||
|         __execute(item_cache_.begin(), item_cache_.end()); | ||||
|         return createIndexedPackGroup(from, to, selector_); | ||||
|     } | ||||
| 
 | ||||
|  | @ -933,37 +960,12 @@ private: | |||
|         return pg; | ||||
|     } | ||||
| 
 | ||||
|     Radians findBestRotation(Item& item) { | ||||
|         opt::StopCriteria stopcr; | ||||
|         stopcr.absolute_score_difference = 0.01; | ||||
|         stopcr.max_iterations = 10000; | ||||
|         opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr); | ||||
| 
 | ||||
|         auto orig_rot = item.rotation(); | ||||
| 
 | ||||
|         auto result = solver.optimize_min([&item, &orig_rot](Radians rot){ | ||||
|             item.rotation(orig_rot + rot); | ||||
|             auto bb = item.boundingBox(); | ||||
|             return std::sqrt(bb.height()*bb.width()); | ||||
|         }, opt::initvals(Radians(0)), opt::bound<Radians>(-Pi/2, Pi/2)); | ||||
| 
 | ||||
|         item.rotation(orig_rot); | ||||
| 
 | ||||
|         return std::get<0>(result.optimum); | ||||
|     } | ||||
| 
 | ||||
|     template<class TIter> inline void __arrange(TIter from, TIter to) | ||||
|     template<class TIter> inline void __execute(TIter from, TIter to) | ||||
|     { | ||||
|         if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { | ||||
|             item.addOffset(static_cast<Unit>(std::ceil(min_obj_distance_/2.0))); | ||||
|         }); | ||||
| 
 | ||||
|         if(use_min_bb_rotation_) | ||||
|             std::for_each(from, to, [this](Item& item){ | ||||
|                 Radians rot = findBestRotation(item); | ||||
|                 item.rotate(rot); | ||||
|             }); | ||||
| 
 | ||||
|         selector_.template packItems<PlacementStrategy>( | ||||
|                     from, to, bin_, pconfig_); | ||||
| 
 | ||||
|  |  | |||
|  | @ -67,11 +67,11 @@ class metaloop { | |||
| // need to wrap that in a type (see metaloop::Int).
 | ||||
| 
 | ||||
| /*
 | ||||
|  * A helper alias to create integer values wrapped as a type. It is nessecary | ||||
|  * A helper alias to create integer values wrapped as a type. It is necessary | ||||
|  * because a non type template parameter (such as int) would be prohibited in | ||||
|  * a partial specialization. Also for the same reason we have to use a class | ||||
|  * _Metaloop instead of a simple function as a functor. A function cannot be | ||||
|  * partially specialized in a way that is neccesary for this trick. | ||||
|  * partially specialized in a way that is necessary for this trick. | ||||
|  */ | ||||
| template<int N> using Int = std::integral_constant<int, N>; | ||||
| 
 | ||||
|  | @ -88,7 +88,7 @@ public: | |||
|     // It takes the real functor that can be specified in-place but only
 | ||||
|     // with C++14 because the second parameter's type will depend on the
 | ||||
|     // type of the parameter pack element that is processed. In C++14 we can
 | ||||
|     // specify this second parameter type as auto in the lamda parameter list.
 | ||||
|     // specify this second parameter type as auto in the lambda parameter list.
 | ||||
|     inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {} | ||||
| 
 | ||||
|     template<class T> void operator ()(T&& pack_element) { | ||||
|  | @ -146,7 +146,7 @@ public: | |||
|  * version of run is called which does not call itself anymore. | ||||
|  * | ||||
|  * If you are utterly annoyed, at least you have learned a super crazy | ||||
|  * functional metaprogramming pattern. | ||||
|  * functional meta-programming pattern. | ||||
|  */ | ||||
| template<class...Args> | ||||
| using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>; | ||||
|  |  | |||
|  | @ -102,6 +102,9 @@ struct StopCriteria { | |||
|     /// If the relative value difference between two scores.
 | ||||
|     double relative_score_difference = std::nan(""); | ||||
| 
 | ||||
|     /// Stop if this value or better is found.
 | ||||
|     double stop_score = std::nan(""); | ||||
| 
 | ||||
|     unsigned max_iterations = 0; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -142,10 +142,12 @@ protected: | |||
|         default: ; | ||||
|         } | ||||
| 
 | ||||
|         auto abs_diff = stopcr_.absolute_score_difference; | ||||
|         auto rel_diff = stopcr_.relative_score_difference; | ||||
|         double abs_diff = stopcr_.absolute_score_difference; | ||||
|         double rel_diff = stopcr_.relative_score_difference; | ||||
|         double stopval = stopcr_.stop_score; | ||||
|         if(!std::isnan(abs_diff)) opt_.set_ftol_abs(abs_diff); | ||||
|         if(!std::isnan(rel_diff)) opt_.set_ftol_rel(rel_diff); | ||||
|         if(!std::isnan(stopval))  opt_.set_stopval(stopval); | ||||
| 
 | ||||
|         if(this->stopcr_.max_iterations > 0) | ||||
|             opt_.set_maxeval(this->stopcr_.max_iterations ); | ||||
|  |  | |||
|  | @ -5,11 +5,26 @@ | |||
| 
 | ||||
| #include "placer_boilerplate.hpp" | ||||
| 
 | ||||
| namespace libnest2d { namespace strategies { | ||||
| namespace libnest2d { namespace placers { | ||||
| 
 | ||||
| template<class T, class = T> struct Epsilon {}; | ||||
| 
 | ||||
| template<class T> | ||||
| struct Epsilon<T, enable_if_t<std::is_integral<T>::value, T> > { | ||||
|     static const T Value = 1; | ||||
| }; | ||||
| 
 | ||||
| template<class T> | ||||
| struct Epsilon<T, enable_if_t<std::is_floating_point<T>::value, T> > { | ||||
|     static const T Value = 1e-3; | ||||
| }; | ||||
| 
 | ||||
| template<class RawShape> | ||||
| struct BLConfig { | ||||
|     TCoord<TPoint<RawShape>> min_obj_distance = 0; | ||||
|     DECLARE_MAIN_TYPES(RawShape); | ||||
| 
 | ||||
|     Coord min_obj_distance = 0; | ||||
|     Coord epsilon = Epsilon<Coord>::Value; | ||||
|     bool allow_rotations = false; | ||||
| }; | ||||
| 
 | ||||
|  | @ -27,9 +42,13 @@ public: | |||
| 
 | ||||
|     explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {} | ||||
| 
 | ||||
|     PackResult trypack(Item& item) { | ||||
|     template<class Range = ConstItemRange<typename Base::DefaultIter>> | ||||
|     PackResult trypack(Item& item, | ||||
|                        const Range& = Range()) | ||||
|     { | ||||
|         auto r = _trypack(item); | ||||
|         if(!r && Base::config_.allow_rotations) { | ||||
| 
 | ||||
|             item.rotate(Degrees(90)); | ||||
|             r =_trypack(item); | ||||
|         } | ||||
|  | @ -65,20 +84,21 @@ protected: | |||
|         setInitialPosition(item); | ||||
| 
 | ||||
|         Unit d = availableSpaceDown(item); | ||||
|         bool can_move = d > 1 /*std::numeric_limits<Unit>::epsilon()*/; | ||||
|         auto eps = config_.epsilon; | ||||
|         bool can_move = d > eps; | ||||
|         bool can_be_packed = can_move; | ||||
|         bool left = true; | ||||
| 
 | ||||
|         while(can_move) { | ||||
|             if(left) { // write previous down move and go down
 | ||||
|                 item.translate({0, -d+1}); | ||||
|                 item.translate({0, -d+eps}); | ||||
|                 d = availableSpaceLeft(item); | ||||
|                 can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/; | ||||
|                 can_move = d > eps; | ||||
|                 left = false; | ||||
|             } else { // write previous left move and go down
 | ||||
|                 item.translate({-d+1, 0}); | ||||
|                 item.translate({-d+eps, 0}); | ||||
|                 d = availableSpaceDown(item); | ||||
|                 can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/; | ||||
|                 can_move = d > eps; | ||||
|                 left = true; | ||||
|             } | ||||
|         } | ||||
|  | @ -112,10 +132,10 @@ protected: | |||
|                   const RawShape& scanpoly) | ||||
|     { | ||||
|         auto tsh = other.transformedShape(); | ||||
|         return ( ShapeLike::intersects(tsh, scanpoly) || | ||||
|                  ShapeLike::isInside(tsh, scanpoly) ) && | ||||
|                ( !ShapeLike::intersects(tsh, item.rawShape()) && | ||||
|                  !ShapeLike::isInside(tsh, item.rawShape()) ); | ||||
|         return ( sl::intersects(tsh, scanpoly) || | ||||
|                  sl::isInside(tsh, scanpoly) ) && | ||||
|                ( !sl::intersects(tsh, item.rawShape()) && | ||||
|                  !sl::isInside(tsh, item.rawShape()) ); | ||||
|     } | ||||
| 
 | ||||
|     template<class C = Coord> | ||||
|  | @ -126,25 +146,25 @@ protected: | |||
|     { | ||||
|         auto tsh = other.transformedShape(); | ||||
| 
 | ||||
|         bool inters_scanpoly = ShapeLike::intersects(tsh, scanpoly) && | ||||
|                 !ShapeLike::touches(tsh, scanpoly); | ||||
|         bool inters_item = ShapeLike::intersects(tsh, item.rawShape()) && | ||||
|                 !ShapeLike::touches(tsh, item.rawShape()); | ||||
|         bool inters_scanpoly = sl::intersects(tsh, scanpoly) && | ||||
|                 !sl::touches(tsh, scanpoly); | ||||
|         bool inters_item = sl::intersects(tsh, item.rawShape()) && | ||||
|                 !sl::touches(tsh, item.rawShape()); | ||||
| 
 | ||||
|         return ( inters_scanpoly || | ||||
|                  ShapeLike::isInside(tsh, scanpoly)) && | ||||
|                  sl::isInside(tsh, scanpoly)) && | ||||
|                ( !inters_item && | ||||
|                  !ShapeLike::isInside(tsh, item.rawShape()) | ||||
|                  !sl::isInside(tsh, item.rawShape()) | ||||
|                  ); | ||||
|     } | ||||
| 
 | ||||
|     Container itemsInTheWayOf(const Item& item, const Dir dir) { | ||||
|     ItemGroup itemsInTheWayOf(const Item& item, const Dir dir) { | ||||
|         // Get the left or down polygon, that has the same area as the shadow
 | ||||
|         // of input item reflected to the left or downwards
 | ||||
|         auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) : | ||||
|                                             downPoly(item); | ||||
| 
 | ||||
|         Container ret;    // packed items 'in the way' of item
 | ||||
|         ItemGroup ret;    // packed items 'in the way' of item
 | ||||
|         ret.reserve(items_.size()); | ||||
| 
 | ||||
|         // Predicate to find items that are 'in the way' for left (down) move
 | ||||
|  | @ -173,18 +193,18 @@ protected: | |||
| 
 | ||||
|         if(dir == Dir::LEFT) { | ||||
|             getCoord = [](const Vertex& v) { return getX(v); }; | ||||
|             availableDistance = PointLike::horizontalDistance<Vertex>; | ||||
|             availableDistance = pointlike::horizontalDistance<Vertex>; | ||||
|             availableDistanceSV = [](const Segment& s, const Vertex& v) { | ||||
|                 auto ret = PointLike::horizontalDistance<Vertex>(v, s); | ||||
|                 auto ret = pointlike::horizontalDistance<Vertex>(v, s); | ||||
|                 if(ret.second) ret.first = -ret.first; | ||||
|                 return ret; | ||||
|             }; | ||||
|         } | ||||
|         else { | ||||
|             getCoord = [](const Vertex& v) { return getY(v); }; | ||||
|             availableDistance = PointLike::verticalDistance<Vertex>; | ||||
|             availableDistance = pointlike::verticalDistance<Vertex>; | ||||
|             availableDistanceSV = [](const Segment& s, const Vertex& v) { | ||||
|                 auto ret = PointLike::verticalDistance<Vertex>(v, s); | ||||
|                 auto ret = pointlike::verticalDistance<Vertex>(v, s); | ||||
|                 if(ret.second) ret.first = -ret.first; | ||||
|                 return ret; | ||||
|             }; | ||||
|  | @ -214,9 +234,9 @@ protected: | |||
|                 assert(pleft.vertexCount() > 0); | ||||
| 
 | ||||
|                 auto trpleft = pleft.transformedShape(); | ||||
|                 auto first = ShapeLike::begin(trpleft); | ||||
|                 auto first = sl::begin(trpleft); | ||||
|                 auto next = first + 1; | ||||
|                 auto endit = ShapeLike::end(trpleft); | ||||
|                 auto endit = sl::end(trpleft); | ||||
| 
 | ||||
|                 while(next != endit) { | ||||
|                     Segment seg(*(first++), *(next++)); | ||||
|  | @ -340,16 +360,16 @@ protected: | |||
| 
 | ||||
|         // reserve for all vertices plus 2 for the left horizontal wall, 2 for
 | ||||
|         // the additional vertices for maintaning min object distance
 | ||||
|         ShapeLike::reserve(rsh, finish-start+4); | ||||
|         sl::reserve(rsh, finish-start+4); | ||||
| 
 | ||||
|         /*auto addOthers = [&rsh, finish, start, &item](){
 | ||||
|             for(size_t i = start+1; i < finish; i++) | ||||
|                 ShapeLike::addVertex(rsh, item.vertex(i)); | ||||
|                 sl::addVertex(rsh, item.vertex(i)); | ||||
|         };*/ | ||||
| 
 | ||||
|         auto reverseAddOthers = [&rsh, finish, start, &item](){ | ||||
|             for(auto i = finish-1; i > start; i--) | ||||
|                 ShapeLike::addVertex(rsh, item.vertex( | ||||
|                 sl::addVertex(rsh, item.vertex( | ||||
|                                          static_cast<unsigned long>(i))); | ||||
|         }; | ||||
| 
 | ||||
|  | @ -361,25 +381,25 @@ protected: | |||
| 
 | ||||
|         // Clockwise polygon construction
 | ||||
| 
 | ||||
|         ShapeLike::addVertex(rsh, topleft_vertex); | ||||
|         sl::addVertex(rsh, topleft_vertex); | ||||
| 
 | ||||
|         if(dir == Dir::LEFT) reverseAddOthers(); | ||||
|         else { | ||||
|             ShapeLike::addVertex(rsh, getX(topleft_vertex), 0); | ||||
|             ShapeLike::addVertex(rsh, getX(bottomleft_vertex), 0); | ||||
|             sl::addVertex(rsh, getX(topleft_vertex), 0); | ||||
|             sl::addVertex(rsh, getX(bottomleft_vertex), 0); | ||||
|         } | ||||
| 
 | ||||
|         ShapeLike::addVertex(rsh, bottomleft_vertex); | ||||
|         sl::addVertex(rsh, bottomleft_vertex); | ||||
| 
 | ||||
|         if(dir == Dir::LEFT) { | ||||
|             ShapeLike::addVertex(rsh, 0, getY(bottomleft_vertex)); | ||||
|             ShapeLike::addVertex(rsh, 0, getY(topleft_vertex)); | ||||
|             sl::addVertex(rsh, 0, getY(bottomleft_vertex)); | ||||
|             sl::addVertex(rsh, 0, getY(topleft_vertex)); | ||||
|         } | ||||
|         else reverseAddOthers(); | ||||
| 
 | ||||
| 
 | ||||
|         // Close the polygon
 | ||||
|         ShapeLike::addVertex(rsh, topleft_vertex); | ||||
|         sl::addVertex(rsh, topleft_vertex); | ||||
| 
 | ||||
|         return rsh; | ||||
|     } | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -3,14 +3,11 @@ | |||
| 
 | ||||
| #include "../libnest2d.hpp" | ||||
| 
 | ||||
| namespace libnest2d { namespace strategies { | ||||
| namespace libnest2d { namespace placers { | ||||
| 
 | ||||
| struct EmptyConfig {}; | ||||
| 
 | ||||
| template<class Subclass, class RawShape, class TBin, | ||||
|          class Cfg = EmptyConfig, | ||||
|          class Store = std::vector<std::reference_wrapper<_Item<RawShape>>> | ||||
|          > | ||||
| template<class Subclass, class RawShape, class TBin, class Cfg = EmptyConfig> | ||||
| class PlacerBoilerplate { | ||||
|     mutable bool farea_valid_ = false; | ||||
|     mutable double farea_ = 0.0; | ||||
|  | @ -22,25 +19,30 @@ public: | |||
|     using Coord = TCoord<Vertex>; | ||||
|     using Unit = Coord; | ||||
|     using Config = Cfg; | ||||
|     using Container = Store; | ||||
|     using ItemGroup = _ItemGroup<Item>; | ||||
|     using DefaultIter = typename ItemGroup::const_iterator; | ||||
| 
 | ||||
|     class PackResult { | ||||
|         Item *item_ptr_; | ||||
|         Vertex move_; | ||||
|         Radians rot_; | ||||
|         double overfit_; | ||||
|         friend class PlacerBoilerplate; | ||||
|         friend Subclass; | ||||
| 
 | ||||
|         PackResult(Item& item): | ||||
|             item_ptr_(&item), | ||||
|             move_(item.translation()), | ||||
|             rot_(item.rotation()) {} | ||||
|         PackResult(): item_ptr_(nullptr) {} | ||||
| 
 | ||||
|         PackResult(double overfit = 1.0): | ||||
|             item_ptr_(nullptr), overfit_(overfit) {} | ||||
| 
 | ||||
|     public: | ||||
|         operator bool() { return item_ptr_ != nullptr; } | ||||
|         double overfit() const { return overfit_; } | ||||
|     }; | ||||
| 
 | ||||
|     using ItemGroup = const Container&; | ||||
| 
 | ||||
|     inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin) | ||||
|     { | ||||
|         items_.reserve(cap); | ||||
|  | @ -56,8 +58,10 @@ public: | |||
|         config_ = config; | ||||
|     } | ||||
| 
 | ||||
|     bool pack(Item& item) { | ||||
|         auto&& r = static_cast<Subclass*>(this)->trypack(item); | ||||
|     template<class Range = ConstItemRange<DefaultIter>> | ||||
|     bool pack(Item& item, | ||||
|               const Range& rem = Range()) { | ||||
|         auto&& r = static_cast<Subclass*>(this)->trypack(item, rem); | ||||
|         if(r) { | ||||
|             items_.push_back(*(r.item_ptr_)); | ||||
|             farea_valid_ = false; | ||||
|  | @ -79,14 +83,11 @@ public: | |||
|         farea_valid_ = false; | ||||
|     } | ||||
| 
 | ||||
|     inline ItemGroup getItems() const { return items_; } | ||||
|     inline const ItemGroup& getItems() const { return items_; } | ||||
| 
 | ||||
|     inline void clearItems() { | ||||
|         items_.clear(); | ||||
|         farea_valid_ = false; | ||||
| #ifndef NDEBUG | ||||
|         debug_items_.clear(); | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|     inline double filledArea() const { | ||||
|  | @ -103,14 +104,10 @@ public: | |||
|         return farea_; | ||||
|     } | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
|     std::vector<Item> debug_items_; | ||||
| #endif | ||||
| 
 | ||||
| protected: | ||||
| 
 | ||||
|     BinType bin_; | ||||
|     Container items_; | ||||
|     ItemGroup items_; | ||||
|     Cfg config_; | ||||
| }; | ||||
| 
 | ||||
|  | @ -121,6 +118,7 @@ using Base::items_;               \ | |||
| using Base::config_;              \ | ||||
| public:                           \ | ||||
| using typename Base::Item;        \ | ||||
| using typename Base::ItemGroup;   \ | ||||
| using typename Base::BinType;     \ | ||||
| using typename Base::Config;      \ | ||||
| using typename Base::Vertex;      \ | ||||
|  | @ -128,7 +126,6 @@ using typename Base::Segment;     \ | |||
| using typename Base::PackResult;  \ | ||||
| using typename Base::Coord;       \ | ||||
| using typename Base::Unit;        \ | ||||
| using typename Base::Container;   \ | ||||
| private: | ||||
| 
 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										41
									
								
								xs/src/libnest2d/libnest2d/rotfinder.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								xs/src/libnest2d/libnest2d/rotfinder.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| #ifndef ROTFINDER_HPP | ||||
| #define ROTFINDER_HPP | ||||
| 
 | ||||
| #include <libnest2d/libnest2d.hpp> | ||||
| #include <libnest2d/optimizer.hpp> | ||||
| #include <iterator> | ||||
| 
 | ||||
| namespace libnest2d { | ||||
| 
 | ||||
| template<class RawShape> | ||||
| Radians findBestRotation(_Item<RawShape>& item) { | ||||
|     opt::StopCriteria stopcr; | ||||
|     stopcr.absolute_score_difference = 0.01; | ||||
|     stopcr.max_iterations = 10000; | ||||
|     opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr); | ||||
| 
 | ||||
|     auto orig_rot = item.rotation(); | ||||
| 
 | ||||
|     auto result = solver.optimize_min([&item, &orig_rot](Radians rot){ | ||||
|         item.rotation(orig_rot + rot); | ||||
|         auto bb = item.boundingBox(); | ||||
|         return std::sqrt(bb.height()*bb.width()); | ||||
|     }, opt::initvals(Radians(0)), opt::bound<Radians>(-Pi/2, Pi/2)); | ||||
| 
 | ||||
|     item.rotation(orig_rot); | ||||
| 
 | ||||
|     return std::get<0>(result.optimum); | ||||
| } | ||||
| 
 | ||||
| template<class Iterator> | ||||
| void findMinimumBoundingBoxRotations(Iterator from, Iterator to) { | ||||
|     using V = typename std::iterator_traits<Iterator>::value_type; | ||||
|     std::for_each(from, to, [](V& item){ | ||||
|         Radians rot = findBestRotation(item); | ||||
|         item.rotate(rot); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // ROTFINDER_HPP
 | ||||
|  | @ -8,7 +8,7 @@ | |||
| 
 | ||||
| #include "selection_boilerplate.hpp" | ||||
| 
 | ||||
| namespace libnest2d { namespace strategies { | ||||
| namespace libnest2d { namespace selections { | ||||
| 
 | ||||
| /**
 | ||||
|  * Selection heuristic based on [López-Camacho]\ | ||||
|  | @ -118,7 +118,7 @@ public: | |||
|         using Placer = PlacementStrategyLike<TPlacer>; | ||||
|         using ItemList = std::list<ItemRef>; | ||||
| 
 | ||||
|         const double bin_area = ShapeLike::area<RawShape>(bin); | ||||
|         const double bin_area = sl::area(bin); | ||||
|         const double w = bin_area * config_.waste_increment; | ||||
| 
 | ||||
|         const double INITIAL_FILL_PROPORTION = config_.initial_fill_proportion; | ||||
|  | @ -227,10 +227,14 @@ public: | |||
|             bool ret = false; | ||||
|             auto it = not_packed.begin(); | ||||
| 
 | ||||
|             auto pack = [&placer, ¬_packed](ItemListIt it) { | ||||
|                 return placer.pack(*it, rem(it, not_packed)); | ||||
|             }; | ||||
| 
 | ||||
|             while(it != not_packed.end() && !ret && | ||||
|                   free_area - (item_area = it->get().area()) <= waste) | ||||
|             { | ||||
|                 if(item_area <= free_area && placer.pack(*it) ) { | ||||
|                 if(item_area <= free_area && pack(it) ) { | ||||
|                     free_area -= item_area; | ||||
|                     filled_area = bin_area - free_area; | ||||
|                     ret = true; | ||||
|  | @ -270,6 +274,11 @@ public: | |||
|             auto it2 = it; | ||||
| 
 | ||||
|             std::vector<TPair> wrong_pairs; | ||||
|             using std::placeholders::_1; | ||||
| 
 | ||||
|             auto trypack = [&placer, ¬_packed](ItemListIt it) { | ||||
|                 return placer.trypack(*it, rem(it, not_packed)); | ||||
|             }; | ||||
| 
 | ||||
|             while(it != endit && !ret && | ||||
|                   free_area - (item_area = it->get().area()) - | ||||
|  | @ -278,7 +287,7 @@ public: | |||
|                 if(item_area + smallestPiece(it, not_packed)->get().area() > | ||||
|                         free_area ) { it++; continue; } | ||||
| 
 | ||||
|                 auto pr = placer.trypack(*it); | ||||
|                 auto pr = trypack(it); | ||||
| 
 | ||||
|                 // First would fit
 | ||||
|                 it2 = not_packed.begin(); | ||||
|  | @ -294,14 +303,14 @@ public: | |||
|                     } | ||||
| 
 | ||||
|                     placer.accept(pr); | ||||
|                     auto pr2 = placer.trypack(*it2); | ||||
|                     auto pr2 = trypack(it2); | ||||
|                     if(!pr2) { | ||||
|                         placer.unpackLast(); // remove first
 | ||||
|                         if(try_reverse) { | ||||
|                             pr2 = placer.trypack(*it2); | ||||
|                             pr2 = trypack(it2); | ||||
|                             if(pr2) { | ||||
|                                 placer.accept(pr2); | ||||
|                                 auto pr12 = placer.trypack(*it); | ||||
|                                 auto pr12 = trypack(it); | ||||
|                                 if(pr12) { | ||||
|                                     placer.accept(pr12); | ||||
|                                     ret = true; | ||||
|  | @ -365,6 +374,14 @@ public: | |||
|                 return it->get().area(); | ||||
|             }; | ||||
| 
 | ||||
|             auto trypack = [&placer, ¬_packed](ItemListIt it) { | ||||
|                 return placer.trypack(*it, rem(it, not_packed)); | ||||
|             }; | ||||
| 
 | ||||
|             auto pack = [&placer, ¬_packed](ItemListIt it) { | ||||
|                 return placer.pack(*it, rem(it, not_packed)); | ||||
|             }; | ||||
| 
 | ||||
|             while (it != endit && !ret) { // drill down 1st level
 | ||||
| 
 | ||||
|                 // We need to determine in each iteration the largest, second
 | ||||
|  | @ -394,7 +411,7 @@ public: | |||
|                     it++; continue; | ||||
|                 } | ||||
| 
 | ||||
|                 auto pr = placer.trypack(*it); | ||||
|                 auto pr = trypack(it); | ||||
| 
 | ||||
|                 // Check for free area and try to pack the 1st item...
 | ||||
|                 if(!pr) { it++; continue; } | ||||
|  | @ -420,15 +437,15 @@ public: | |||
|                     bool can_pack2 = false; | ||||
| 
 | ||||
|                     placer.accept(pr); | ||||
|                     auto pr2 = placer.trypack(*it2); | ||||
|                     auto pr2 = trypack(it2); | ||||
|                     auto pr12 = pr; | ||||
|                     if(!pr2) { | ||||
|                         placer.unpackLast(); // remove first
 | ||||
|                         if(try_reverse) { | ||||
|                             pr2 = placer.trypack(*it2); | ||||
|                             pr2 = trypack(it2); | ||||
|                             if(pr2) { | ||||
|                                 placer.accept(pr2); | ||||
|                                 pr12 = placer.trypack(*it); | ||||
|                                 pr12 = trypack(it); | ||||
|                                 if(pr12) can_pack2 = true; | ||||
|                                 placer.unpackLast(); | ||||
|                             } | ||||
|  | @ -463,7 +480,7 @@ public: | |||
|                         if(a3_sum > free_area) { it3++; continue; } | ||||
| 
 | ||||
|                         placer.accept(pr12); placer.accept(pr2); | ||||
|                         bool can_pack3 = placer.pack(*it3); | ||||
|                         bool can_pack3 = pack(it3); | ||||
| 
 | ||||
|                         if(!can_pack3) { | ||||
|                             placer.unpackLast(); | ||||
|  | @ -473,16 +490,16 @@ public: | |||
|                         if(!can_pack3 && try_reverse) { | ||||
| 
 | ||||
|                             std::array<size_t, 3> indices = {0, 1, 2}; | ||||
|                             std::array<ItemRef, 3> | ||||
|                                     candidates = {*it, *it2, *it3}; | ||||
|                             std::array<typename ItemList::iterator, 3> | ||||
|                                     candidates = {it, it2, it3}; | ||||
| 
 | ||||
|                             auto tryPack = [&placer, &candidates]( | ||||
|                             auto tryPack = [&placer, &candidates, &pack]( | ||||
|                                     const decltype(indices)& idx) | ||||
|                             { | ||||
|                                 std::array<bool, 3> packed = {false}; | ||||
| 
 | ||||
|                                 for(auto id : idx) packed.at(id) = | ||||
|                                         placer.pack(candidates[id]); | ||||
|                                         pack(candidates[id]); | ||||
| 
 | ||||
|                                 bool check = | ||||
|                                 std::all_of(packed.begin(), | ||||
|  | @ -536,7 +553,7 @@ public: | |||
|         { auto it = store_.begin(); | ||||
|             while (it != store_.end()) { | ||||
|                 Placer p(bin); p.configure(pconfig); | ||||
|                 if(!p.pack(*it)) { | ||||
|                 if(!p.pack(*it, rem(it, store_))) { | ||||
|                     it = store_.erase(it); | ||||
|                 } else it++; | ||||
|             } | ||||
|  | @ -551,11 +568,7 @@ public: | |||
|         { | ||||
| 
 | ||||
|             packed_bins_[idx] = placer.getItems(); | ||||
| #ifndef NDEBUG | ||||
|             packed_bins_[idx].insert(packed_bins_[idx].end(), | ||||
|                                        placer.getDebugItems().begin(), | ||||
|                                        placer.getDebugItems().end()); | ||||
| #endif | ||||
| 
 | ||||
|             // TODO here should be a spinlock
 | ||||
|             slock.lock(); | ||||
|             acounter -= packednum; | ||||
|  | @ -601,7 +614,7 @@ public: | |||
|                     while(it != not_packed.end() && | ||||
|                           filled_area < INITIAL_FILL_AREA) | ||||
|                     { | ||||
|                         if(placer.pack(*it)) { | ||||
|                         if(placer.pack(*it, rem(it, not_packed))) { | ||||
|                             filled_area += it->get().area(); | ||||
|                             free_area = bin_area - filled_area; | ||||
|                             it = not_packed.erase(it); | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| 
 | ||||
| #include "selection_boilerplate.hpp" | ||||
| 
 | ||||
| namespace libnest2d { namespace strategies { | ||||
| namespace libnest2d { namespace selections { | ||||
| 
 | ||||
| template<class RawShape> | ||||
| class _FillerSelection: public SelectionBoilerplate<RawShape> { | ||||
|  | @ -56,18 +56,13 @@ public: | |||
| 
 | ||||
|         std::sort(store_.begin(), store_.end(), sortfunc); | ||||
| 
 | ||||
| //        Container a = {store_[0], store_[1], store_[4], store_[5] };
 | ||||
| ////        a.insert(a.end(), store_.end()-10, store_.end());
 | ||||
| //        store_ = a;
 | ||||
| 
 | ||||
|         PlacementStrategyLike<TPlacer> placer(bin); | ||||
|         placer.configure(pconfig); | ||||
| 
 | ||||
|         auto it = store_.begin(); | ||||
|         while(it != store_.end()) { | ||||
|             if(!placer.pack(*it))  { | ||||
|             if(!placer.pack(*it, {std::next(it), store_.end()}))  { | ||||
|                 if(packed_bins_.back().empty()) ++it; | ||||
| //                makeProgress(placer);
 | ||||
|                 placer.clearItems(); | ||||
|                 packed_bins_.emplace_back(); | ||||
|             } else { | ||||
|  | @ -76,9 +71,6 @@ public: | |||
|             } | ||||
|         } | ||||
| 
 | ||||
| //        if(was_packed) {
 | ||||
| //            packed_bins_.push_back(placer.getItems());
 | ||||
| //        }
 | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| #include "../libnest2d.hpp" | ||||
| #include "selection_boilerplate.hpp" | ||||
| 
 | ||||
| namespace libnest2d { namespace strategies { | ||||
| namespace libnest2d { namespace selections { | ||||
| 
 | ||||
| template<class RawShape> | ||||
| class _FirstFitSelection: public SelectionBoilerplate<RawShape> { | ||||
|  | @ -40,6 +40,7 @@ public: | |||
|         packed_bins_.clear(); | ||||
| 
 | ||||
|         std::vector<Placer> placers; | ||||
|         placers.reserve(last-first); | ||||
| 
 | ||||
|         std::copy(first, last, std::back_inserter(store_)); | ||||
| 
 | ||||
|  | @ -66,21 +67,25 @@ public: | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         for(auto& item : store_ ) { | ||||
|             bool was_packed = false; | ||||
|             while(!was_packed) { | ||||
|         auto it = store_.begin(); | ||||
| 
 | ||||
|                 for(size_t j = 0; j < placers.size() && !was_packed; j++) { | ||||
|                     if((was_packed = placers[j].pack(item))) | ||||
|                         makeProgress(placers[j], j); | ||||
|         while(it != store_.end()) { | ||||
|             bool was_packed = false; | ||||
|             size_t j = 0; | ||||
|             while(!was_packed) { | ||||
|                 for(; j < placers.size() && !was_packed; j++) { | ||||
|                     if((was_packed = placers[j].pack(*it, rem(it, store_) ))) | ||||
|                             makeProgress(placers[j], j); | ||||
|                 } | ||||
| 
 | ||||
|                 if(!was_packed) { | ||||
|                     placers.emplace_back(bin); | ||||
|                     placers.back().configure(pconfig); | ||||
|                     packed_bins_.emplace_back(); | ||||
|                     j = placers.size() - 1; | ||||
|                 } | ||||
|             } | ||||
|             ++it; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,8 +3,7 @@ | |||
| 
 | ||||
| #include "../libnest2d.hpp" | ||||
| 
 | ||||
| namespace libnest2d { | ||||
| namespace strategies { | ||||
| namespace libnest2d { namespace selections { | ||||
| 
 | ||||
| template<class RawShape> | ||||
| class SelectionBoilerplate { | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include "printer_parts.h" | ||||
| #include <libnest2d/geometry_traits_nfp.hpp> | ||||
| //#include "../tools/libnfpglue.hpp"
 | ||||
| //#include "../tools/nfp_svgnest_glue.hpp"
 | ||||
| 
 | ||||
| std::vector<libnest2d::Item>& prusaParts() { | ||||
|     static std::vector<libnest2d::Item> ret; | ||||
|  | @ -99,6 +100,43 @@ TEST(BasicFunctionality, creationAndDestruction) | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| TEST(GeometryAlgorithms, boundingCircle) { | ||||
|     using namespace libnest2d; | ||||
|     using placers::boundingCircle; | ||||
| 
 | ||||
|     PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}}; | ||||
|     Circle c = boundingCircle(p); | ||||
| 
 | ||||
|     ASSERT_EQ(c.center().X, 0); | ||||
|     ASSERT_EQ(c.center().Y, 0); | ||||
|     ASSERT_DOUBLE_EQ(c.radius(), 10); | ||||
| 
 | ||||
|     shapelike::translate(p, PointImpl{10, 10}); | ||||
|     c = boundingCircle(p); | ||||
| 
 | ||||
|     ASSERT_EQ(c.center().X, 10); | ||||
|     ASSERT_EQ(c.center().Y, 10); | ||||
|     ASSERT_DOUBLE_EQ(c.radius(), 10); | ||||
| 
 | ||||
|     auto parts = prusaParts(); | ||||
| 
 | ||||
|     int i = 0; | ||||
|     for(auto& part : parts) { | ||||
|         c = boundingCircle(part.transformedShape()); | ||||
|         if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl; | ||||
| 
 | ||||
|         else for(auto v : shapelike::getContour(part.transformedShape()) ) { | ||||
|             auto d = pointlike::distance(v, c.center()); | ||||
|             if(d > c.radius() ) { | ||||
|                 auto e = std::abs( 1.0 - d/c.radius()); | ||||
|                 ASSERT_LE(e, 1e-3); | ||||
|             } | ||||
|         } | ||||
|         i++; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| TEST(GeometryAlgorithms, Distance) { | ||||
|     using namespace libnest2d; | ||||
| 
 | ||||
|  | @ -107,14 +145,14 @@ TEST(GeometryAlgorithms, Distance) { | |||
|     Point p2 = {10, 0}; | ||||
|     Point p3 = {10, 10}; | ||||
| 
 | ||||
|     ASSERT_DOUBLE_EQ(PointLike::distance(p1, p2), 10); | ||||
|     ASSERT_DOUBLE_EQ(PointLike::distance(p1, p3), sqrt(200)); | ||||
|     ASSERT_DOUBLE_EQ(pointlike::distance(p1, p2), 10); | ||||
|     ASSERT_DOUBLE_EQ(pointlike::distance(p1, p3), sqrt(200)); | ||||
| 
 | ||||
|     Segment seg(p1, p3); | ||||
| 
 | ||||
|     ASSERT_DOUBLE_EQ(PointLike::distance(p2, seg), 7.0710678118654755); | ||||
|     ASSERT_DOUBLE_EQ(pointlike::distance(p2, seg), 7.0710678118654755); | ||||
| 
 | ||||
|     auto result = PointLike::horizontalDistance(p2, seg); | ||||
|     auto result = pointlike::horizontalDistance(p2, seg); | ||||
| 
 | ||||
|     auto check = [](Coord val, Coord expected) { | ||||
|         if(std::is_floating_point<Coord>::value) | ||||
|  | @ -127,11 +165,11 @@ TEST(GeometryAlgorithms, Distance) { | |||
|     ASSERT_TRUE(result.second); | ||||
|     check(result.first, 10); | ||||
| 
 | ||||
|     result = PointLike::verticalDistance(p2, seg); | ||||
|     result = pointlike::verticalDistance(p2, seg); | ||||
|     ASSERT_TRUE(result.second); | ||||
|     check(result.first, -10); | ||||
| 
 | ||||
|     result = PointLike::verticalDistance(Point{10, 20}, seg); | ||||
|     result = pointlike::verticalDistance(Point{10, 20}, seg); | ||||
|     ASSERT_TRUE(result.second); | ||||
|     check(result.first, 10); | ||||
| 
 | ||||
|  | @ -139,12 +177,12 @@ TEST(GeometryAlgorithms, Distance) { | |||
|     Point p4 = {80, 0}; | ||||
|     Segment seg2 = { {0, 0}, {0, 40} }; | ||||
| 
 | ||||
|     result = PointLike::horizontalDistance(p4, seg2); | ||||
|     result = pointlike::horizontalDistance(p4, seg2); | ||||
| 
 | ||||
|     ASSERT_TRUE(result.second); | ||||
|     check(result.first, 80); | ||||
| 
 | ||||
|     result = PointLike::verticalDistance(p4, seg2); | ||||
|     result = pointlike::verticalDistance(p4, seg2); | ||||
|     // Point should not be related to the segment
 | ||||
|     ASSERT_FALSE(result.second); | ||||
| 
 | ||||
|  | @ -172,7 +210,7 @@ TEST(GeometryAlgorithms, Area) { | |||
|         {61, 97} | ||||
|     }; | ||||
| 
 | ||||
|     ASSERT_TRUE(ShapeLike::area(item.transformedShape()) > 0 ); | ||||
|     ASSERT_TRUE(shapelike::area(item.transformedShape()) > 0 ); | ||||
| } | ||||
| 
 | ||||
| TEST(GeometryAlgorithms, IsPointInsidePolygon) { | ||||
|  | @ -182,21 +220,21 @@ TEST(GeometryAlgorithms, IsPointInsidePolygon) { | |||
| 
 | ||||
|     Point p = {1, 1}; | ||||
| 
 | ||||
|     ASSERT_TRUE(rect.isPointInside(p)); | ||||
|     ASSERT_TRUE(rect.isInside(p)); | ||||
| 
 | ||||
|     p = {11, 11}; | ||||
| 
 | ||||
|     ASSERT_FALSE(rect.isPointInside(p)); | ||||
|     ASSERT_FALSE(rect.isInside(p)); | ||||
| 
 | ||||
| 
 | ||||
|     p = {11, 12}; | ||||
| 
 | ||||
|     ASSERT_FALSE(rect.isPointInside(p)); | ||||
|     ASSERT_FALSE(rect.isInside(p)); | ||||
| 
 | ||||
| 
 | ||||
|     p = {3, 3}; | ||||
| 
 | ||||
|     ASSERT_TRUE(rect.isPointInside(p)); | ||||
|     ASSERT_TRUE(rect.isInside(p)); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  | @ -250,7 +288,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon) | |||
| 
 | ||||
|     Item leftp(placer.leftPoly(item)); | ||||
| 
 | ||||
|     ASSERT_TRUE(ShapeLike::isValid(leftp.rawShape()).first); | ||||
|     ASSERT_TRUE(shapelike::isValid(leftp.rawShape()).first); | ||||
|     ASSERT_EQ(leftp.vertexCount(), leftControl.vertexCount()); | ||||
| 
 | ||||
|     for(unsigned long i = 0; i < leftControl.vertexCount(); i++) { | ||||
|  | @ -260,7 +298,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon) | |||
| 
 | ||||
|     Item downp(placer.downPoly(item)); | ||||
| 
 | ||||
|     ASSERT_TRUE(ShapeLike::isValid(downp.rawShape()).first); | ||||
|     ASSERT_TRUE(shapelike::isValid(downp.rawShape()).first); | ||||
|     ASSERT_EQ(downp.vertexCount(), downControl.vertexCount()); | ||||
| 
 | ||||
|     for(unsigned long i = 0; i < downControl.vertexCount(); i++) { | ||||
|  | @ -297,7 +335,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight) | |||
|         {20, 20} }; | ||||
| 
 | ||||
| 
 | ||||
|     Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250)); | ||||
|     Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250)); | ||||
| 
 | ||||
|     auto groups = arrange(rects.begin(), rects.end()); | ||||
| 
 | ||||
|  | @ -350,7 +388,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose) | |||
| 
 | ||||
|     Coord min_obj_distance = 5; | ||||
| 
 | ||||
|     Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250), | ||||
|     Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250), | ||||
|                                                      min_obj_distance); | ||||
| 
 | ||||
|     auto groups = arrange(rects.begin(), rects.end()); | ||||
|  | @ -401,7 +439,7 @@ R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | |||
|                 setX(v, getX(v)/SCALE); | ||||
|                 rbin.setVertex(i, v); | ||||
|             } | ||||
|             out << ShapeLike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl; | ||||
|             out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl; | ||||
|             for(Item& sh : r) { | ||||
|                 Item tsh(sh.transformedShape()); | ||||
|                 for(unsigned i = 0; i < tsh.vertexCount(); i++) { | ||||
|  | @ -410,7 +448,7 @@ R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | |||
|                     setX(v, getX(v)/SCALE); | ||||
|                     tsh.setVertex(i, v); | ||||
|                 } | ||||
|                 out << ShapeLike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl; | ||||
|                 out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl; | ||||
|             } | ||||
|             out << "\n</svg>" << std::endl; | ||||
|         } | ||||
|  | @ -664,7 +702,7 @@ std::vector<ItemPair> nfp_concave_testdata = { | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| template<NfpLevel lvl, Coord SCALE> | ||||
| template<nfp::NfpLevel lvl, Coord SCALE> | ||||
| void testNfp(const std::vector<ItemPair>& testdata) { | ||||
|     using namespace libnest2d; | ||||
| 
 | ||||
|  | @ -674,29 +712,33 @@ void testNfp(const std::vector<ItemPair>& testdata) { | |||
| 
 | ||||
|     auto& exportfun = exportSVG<SCALE, Box>; | ||||
| 
 | ||||
|     auto onetest = [&](Item& orbiter, Item& stationary){ | ||||
|     auto onetest = [&](Item& orbiter, Item& stationary, unsigned testidx){ | ||||
|         testcase++; | ||||
| 
 | ||||
|         orbiter.translate({210*SCALE, 0}); | ||||
| 
 | ||||
|         auto&& nfp = Nfp::noFitPolygon<lvl>(stationary.rawShape(), | ||||
|         auto&& nfp = nfp::noFitPolygon<lvl>(stationary.rawShape(), | ||||
|                                             orbiter.transformedShape()); | ||||
| 
 | ||||
|         strategies::correctNfpPosition(nfp, stationary, orbiter); | ||||
|         placers::correctNfpPosition(nfp, stationary, orbiter); | ||||
| 
 | ||||
|         auto v = ShapeLike::isValid(nfp.first); | ||||
|         auto valid = shapelike::isValid(nfp.first); | ||||
| 
 | ||||
|         if(!v.first) { | ||||
|             std::cout << v.second << std::endl; | ||||
|         } | ||||
|         /*Item infp(nfp.first);
 | ||||
|         if(!valid.first) { | ||||
|             std::cout << "test instance: " << testidx << " " | ||||
|                       << valid.second << std::endl; | ||||
|             std::vector<std::reference_wrapper<Item>> inp = {std::ref(infp)}; | ||||
|             exportfun(inp, bin, testidx); | ||||
|         }*/ | ||||
| 
 | ||||
|         ASSERT_TRUE(v.first); | ||||
|         ASSERT_TRUE(valid.first); | ||||
| 
 | ||||
|         Item infp(nfp.first); | ||||
| 
 | ||||
|         int i = 0; | ||||
|         auto rorbiter = orbiter.transformedShape(); | ||||
|         auto vo = Nfp::referenceVertex(rorbiter); | ||||
|         auto vo = nfp::referenceVertex(rorbiter); | ||||
| 
 | ||||
|         ASSERT_TRUE(stationary.isInside(infp)); | ||||
| 
 | ||||
|  | @ -710,7 +752,7 @@ void testNfp(const std::vector<ItemPair>& testdata) { | |||
| 
 | ||||
|             bool touching = Item::touches(tmp, stationary); | ||||
| 
 | ||||
|             if(!touching) { | ||||
|             if(!touching || !valid.first) { | ||||
|                 std::vector<std::reference_wrapper<Item>> inp = { | ||||
|                     std::ref(stationary), std::ref(tmp), std::ref(infp) | ||||
|                 }; | ||||
|  | @ -722,22 +764,24 @@ void testNfp(const std::vector<ItemPair>& testdata) { | |||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     unsigned tidx = 0; | ||||
|     for(auto& td : testdata) { | ||||
|         auto orbiter = td.orbiter; | ||||
|         auto stationary = td.stationary; | ||||
|         onetest(orbiter, stationary); | ||||
|         onetest(orbiter, stationary, tidx++); | ||||
|     } | ||||
| 
 | ||||
|     tidx = 0; | ||||
|     for(auto& td : testdata) { | ||||
|         auto orbiter = td.stationary; | ||||
|         auto stationary = td.orbiter; | ||||
|         onetest(orbiter, stationary); | ||||
|         onetest(orbiter, stationary, tidx++); | ||||
|     } | ||||
| } | ||||
| } | ||||
| 
 | ||||
| TEST(GeometryAlgorithms, nfpConvexConvex) { | ||||
|     testNfp<NfpLevel::CONVEX_ONLY, 1>(nfp_testdata); | ||||
|     testNfp<nfp::NfpLevel::CONVEX_ONLY, 1>(nfp_testdata); | ||||
| } | ||||
| 
 | ||||
| //TEST(GeometryAlgorithms, nfpConcaveConcave) {
 | ||||
|  | @ -758,7 +802,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) { | |||
| 
 | ||||
|     Rectangle input(10, 10); | ||||
| 
 | ||||
|     strategies::EdgeCache<PolygonImpl> ecache(input); | ||||
|     placers::EdgeCache<PolygonImpl> ecache(input); | ||||
| 
 | ||||
|     auto first = *input.begin(); | ||||
|     ASSERT_TRUE(getX(first) == getX(ecache.coords(0))); | ||||
|  | @ -770,7 +814,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) { | |||
| 
 | ||||
|     for(int i = 0; i <= 100; i++) { | ||||
|         auto v = ecache.coords(i*(0.01)); | ||||
|         ASSERT_TRUE(ShapeLike::touches(v, input.transformedShape())); | ||||
|         ASSERT_TRUE(shapelike::touches(v, input.transformedShape())); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -784,17 +828,17 @@ TEST(GeometryAlgorithms, mergePileWithPolygon) { | |||
|     rect2.translate({10, 0}); | ||||
|     rect3.translate({25, 0}); | ||||
| 
 | ||||
|     ShapeLike::Shapes<PolygonImpl> pile; | ||||
|     shapelike::Shapes<PolygonImpl> pile; | ||||
|     pile.push_back(rect1.transformedShape()); | ||||
|     pile.push_back(rect2.transformedShape()); | ||||
| 
 | ||||
|     auto result = Nfp::merge(pile, rect3.transformedShape()); | ||||
|     auto result = nfp::merge(pile, rect3.transformedShape()); | ||||
| 
 | ||||
|     ASSERT_EQ(result.size(), 1); | ||||
| 
 | ||||
|     Rectangle ref(45, 15); | ||||
| 
 | ||||
|     ASSERT_EQ(ShapeLike::area(result.front()), ref.area()); | ||||
|     ASSERT_EQ(shapelike::area(result.front()), ref.area()); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char **argv) { | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) { | |||
| 
 | ||||
| NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) | ||||
| { | ||||
|     using Vertex = PointImpl; | ||||
|     namespace sl = shapelike; | ||||
| 
 | ||||
|     NfpR ret; | ||||
| 
 | ||||
|  | @ -85,7 +85,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) | |||
|         // this can throw
 | ||||
|         auto nfp = libnfporb::generateNFP(pstat, porb, true); | ||||
| 
 | ||||
|         auto &ct = ShapeLike::getContour(ret.first); | ||||
|         auto &ct = sl::getContour(ret.first); | ||||
|         ct.reserve(nfp.front().size()+1); | ||||
|         for(auto v : nfp.front()) { | ||||
|             v = scale(v, refactor); | ||||
|  | @ -94,7 +94,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) | |||
|         ct.push_back(ct.front()); | ||||
|         std::reverse(ct.begin(), ct.end()); | ||||
| 
 | ||||
|         auto &rholes = ShapeLike::holes(ret.first); | ||||
|         auto &rholes = sl::holes(ret.first); | ||||
|         for(size_t hidx = 1; hidx < nfp.size(); ++hidx) { | ||||
|             if(nfp[hidx].size() >= 3) { | ||||
|                 rholes.emplace_back(); | ||||
|  | @ -110,31 +110,31 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         ret.second = Nfp::referenceVertex(ret.first); | ||||
|         ret.second = nfp::referenceVertex(ret.first); | ||||
| 
 | ||||
|     } catch(std::exception& e) { | ||||
|         std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl; | ||||
| //        auto ch_stat = ShapeLike::convexHull(sh);
 | ||||
| //        auto ch_orb = ShapeLike::convexHull(cother);
 | ||||
|         ret = Nfp::nfpConvexOnly(sh, cother); | ||||
|         ret = nfp::nfpConvexOnly(sh, cother); | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY>::operator()( | ||||
| NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY>::operator()( | ||||
|         const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) | ||||
| { | ||||
|     return _nfp(sh, cother);//nfpConvexOnly(sh, cother);
 | ||||
| } | ||||
| 
 | ||||
| NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX>::operator()( | ||||
| NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX>::operator()( | ||||
|         const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) | ||||
| { | ||||
|     return _nfp(sh, cother); | ||||
| } | ||||
| 
 | ||||
| NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE>::operator()( | ||||
| NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE>::operator()( | ||||
|         const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother) | ||||
| { | ||||
|     return _nfp(sh, cother); | ||||
|  |  | |||
|  | @ -5,22 +5,22 @@ | |||
| 
 | ||||
| namespace libnest2d { | ||||
| 
 | ||||
| using NfpR = Nfp::NfpResult<PolygonImpl>; | ||||
| using NfpR = nfp::NfpResult<PolygonImpl>; | ||||
| 
 | ||||
| NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother); | ||||
| 
 | ||||
| template<> | ||||
| struct Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> { | ||||
| struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY> { | ||||
|     NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> { | ||||
| struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX> { | ||||
|     NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> { | ||||
| struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE> { | ||||
|     NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); | ||||
| }; | ||||
| 
 | ||||
|  | @ -34,7 +34,7 @@ struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> { | |||
| //    NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
 | ||||
| //};
 | ||||
| 
 | ||||
| template<> struct Nfp::MaxNfpLevel<PolygonImpl> { | ||||
| template<> struct nfp::MaxNfpLevel<PolygonImpl> { | ||||
|     static const BP2D_CONSTEXPR NfpLevel value = | ||||
| //            NfpLevel::CONVEX_ONLY;
 | ||||
|             NfpLevel::BOTH_CONCAVE; | ||||
|  |  | |||
							
								
								
									
										1018
									
								
								xs/src/libnest2d/tools/nfp_svgnest.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1018
									
								
								xs/src/libnest2d/tools/nfp_svgnest.hpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										75
									
								
								xs/src/libnest2d/tools/nfp_svgnest_glue.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								xs/src/libnest2d/tools/nfp_svgnest_glue.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,75 @@ | |||
| #ifndef NFP_SVGNEST_GLUE_HPP | ||||
| #define NFP_SVGNEST_GLUE_HPP | ||||
| 
 | ||||
| #include "nfp_svgnest.hpp" | ||||
| 
 | ||||
| #include <libnest2d/clipper_backend/clipper_backend.hpp> | ||||
| 
 | ||||
| namespace libnest2d { | ||||
| 
 | ||||
| namespace __svgnest { | ||||
| 
 | ||||
| //template<> struct _Tol<double> {
 | ||||
| //    static const BP2D_CONSTEXPR TCoord<PointImpl> Value = 1000000;
 | ||||
| //};
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| namespace nfp { | ||||
| 
 | ||||
| using NfpR = NfpResult<PolygonImpl>; | ||||
| 
 | ||||
| template<> struct NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> { | ||||
|     NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) { | ||||
| //        return nfpConvexOnly(sh, cother);
 | ||||
|         namespace sl = shapelike; | ||||
|         using alg = __svgnest::_alg<PolygonImpl>; | ||||
| 
 | ||||
|         auto nfp_p = alg::noFitPolygon(sl::getContour(sh), | ||||
|                                        sl::getContour(cother), false, false); | ||||
| 
 | ||||
|         PolygonImpl nfp_cntr; | ||||
|         if(!nfp_p.empty()) nfp_cntr.Contour = nfp_p.front(); | ||||
|         return {nfp_cntr, referenceVertex(nfp_cntr)}; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template<> struct NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> { | ||||
|     NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) { | ||||
| //        return nfpConvexOnly(sh, cother);
 | ||||
|         namespace sl = shapelike; | ||||
|         using alg = __svgnest::_alg<PolygonImpl>; | ||||
| 
 | ||||
|         std::cout << "Itt vagyok" << std::endl; | ||||
|         auto nfp_p = alg::noFitPolygon(sl::getContour(sh), | ||||
|                                        sl::getContour(cother), false, false); | ||||
| 
 | ||||
|         PolygonImpl nfp_cntr; | ||||
|         nfp_cntr.Contour = nfp_p.front(); | ||||
|         return {nfp_cntr, referenceVertex(nfp_cntr)}; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> { | ||||
|     NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) { | ||||
|         namespace sl = shapelike; | ||||
|         using alg = __svgnest::_alg<PolygonImpl>; | ||||
| 
 | ||||
|         auto nfp_p = alg::noFitPolygon(sl::getContour(sh), | ||||
|                                        sl::getContour(cother), true, false); | ||||
| 
 | ||||
|         PolygonImpl nfp_cntr; | ||||
|         nfp_cntr.Contour = nfp_p.front(); | ||||
|         return {nfp_cntr, referenceVertex(nfp_cntr)}; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template<> struct MaxNfpLevel<PolygonImpl> { | ||||
| //    static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::BOTH_CONCAVE;
 | ||||
|         static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY; | ||||
| }; | ||||
| 
 | ||||
| }} | ||||
| 
 | ||||
| #endif // NFP_SVGNEST_GLUE_HPP
 | ||||
|  | @ -56,14 +56,14 @@ public: | |||
|             auto d = static_cast<Coord>( | ||||
|                         std::round(conf_.height*conf_.mm_in_coord_units) ); | ||||
| 
 | ||||
|             auto& contour = ShapeLike::getContour(tsh); | ||||
|             auto& contour = shapelike::getContour(tsh); | ||||
|             for(auto& v : contour) setY(v, -getY(v) + d); | ||||
| 
 | ||||
|             auto& holes = ShapeLike::holes(tsh); | ||||
|             auto& holes = shapelike::holes(tsh); | ||||
|             for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d); | ||||
| 
 | ||||
|         } | ||||
|         currentLayer() += ShapeLike::serialize<Formats::SVG>(tsh, | ||||
|         currentLayer() += shapelike::serialize<Formats::SVG>(tsh, | ||||
|                                             1.0/conf_.mm_in_coord_units) + "\n"; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -99,54 +99,56 @@ namespace bgi = boost::geometry::index; | |||
| 
 | ||||
| using SpatElement = std::pair<Box, unsigned>; | ||||
| using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; | ||||
| using ItemGroup = std::vector<std::reference_wrapper<Item>>; | ||||
| template<class TBin> | ||||
| using TPacker = typename placers::_NofitPolyPlacer<PolygonImpl, TBin>; | ||||
| 
 | ||||
| const double BIG_ITEM_TRESHOLD = 0.02; | ||||
| 
 | ||||
| Box boundingBox(const Box& pilebb, const Box& ibb ) { | ||||
|     auto& pminc = pilebb.minCorner(); | ||||
|     auto& pmaxc = pilebb.maxCorner(); | ||||
|     auto& iminc = ibb.minCorner(); | ||||
|     auto& imaxc = ibb.maxCorner(); | ||||
|     PointImpl minc, maxc; | ||||
| 
 | ||||
|     setX(minc, std::min(getX(pminc), getX(iminc))); | ||||
|     setY(minc, std::min(getY(pminc), getY(iminc))); | ||||
| 
 | ||||
|     setX(maxc, std::max(getX(pmaxc), getX(imaxc))); | ||||
|     setY(maxc, std::max(getY(pmaxc), getY(imaxc))); | ||||
|     return Box(minc, maxc); | ||||
| } | ||||
| 
 | ||||
| std::tuple<double /*score*/, Box /*farthest point from bin center*/> | ||||
| objfunc(const PointImpl& bincenter, | ||||
|         double /*bin_area*/, | ||||
|         ShapeLike::Shapes<PolygonImpl>& pile,   // The currently arranged pile
 | ||||
|         double /*pile_area*/, | ||||
|         const shapelike::Shapes<PolygonImpl>& merged_pile, | ||||
|         const Box& pilebb, | ||||
|         const ItemGroup& items, | ||||
|         const Item &item, | ||||
|         double bin_area, | ||||
|         double norm,            // A norming factor for physical dimensions
 | ||||
|         std::vector<double>& areacache, // pile item areas will be cached
 | ||||
|         // a spatial index to quickly get neighbors of the candidate item
 | ||||
|         SpatIndex& spatindex | ||||
|         const SpatIndex& spatindex, | ||||
|         const SpatIndex& smalls_spatindex, | ||||
|         const ItemGroup& remaining | ||||
|         ) | ||||
| { | ||||
|     using pl = PointLike; | ||||
|     using sl = ShapeLike; | ||||
|     using Coord = TCoord<PointImpl>; | ||||
| 
 | ||||
|     static const double BIG_ITEM_TRESHOLD = 0.2; | ||||
|     static const double ROUNDNESS_RATIO = 0.5; | ||||
|     static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; | ||||
| 
 | ||||
|     // We will treat big items (compared to the print bed) differently
 | ||||
|     auto normarea = [norm](double area) { return std::sqrt(area)/norm; }; | ||||
| 
 | ||||
|     // If a new bin has been created:
 | ||||
|     if(pile.size() < areacache.size()) { | ||||
|         areacache.clear(); | ||||
|         spatindex.clear(); | ||||
|     } | ||||
| 
 | ||||
|     // We must fill the caches:
 | ||||
|     int idx = 0; | ||||
|     for(auto& p : pile) { | ||||
|         if(idx == areacache.size()) { | ||||
|             areacache.emplace_back(sl::area(p)); | ||||
|             if(normarea(areacache[idx]) > BIG_ITEM_TRESHOLD) | ||||
|                 spatindex.insert({sl::boundingBox(p), idx}); | ||||
|         } | ||||
| 
 | ||||
|         idx++; | ||||
|     } | ||||
|     auto isBig = [bin_area](double a) { | ||||
|         return a/bin_area > BIG_ITEM_TRESHOLD ; | ||||
|     }; | ||||
| 
 | ||||
|     // Candidate item bounding box
 | ||||
|     auto ibb = item.boundingBox(); | ||||
|     auto ibb = sl::boundingBox(item.transformedShape()); | ||||
| 
 | ||||
|     // Calculate the full bounding box of the pile with the candidate item
 | ||||
|     pile.emplace_back(item.transformedShape()); | ||||
|     auto fullbb = ShapeLike::boundingBox(pile); | ||||
|     pile.pop_back(); | ||||
|     auto fullbb = boundingBox(pilebb, ibb); | ||||
| 
 | ||||
|     // The bounding box of the big items (they will accumulate in the center
 | ||||
|     // of the pile
 | ||||
|  | @ -157,19 +159,11 @@ objfunc(const PointImpl& bincenter, | |||
|         boost::geometry::convert(boostbb, bigbb); | ||||
|     } | ||||
| 
 | ||||
|     // The size indicator of the candidate item. This is not the area,
 | ||||
|     // but almost...
 | ||||
|     double item_normarea = normarea(item.area()); | ||||
| 
 | ||||
|     // Will hold the resulting score
 | ||||
|     double score = 0; | ||||
| 
 | ||||
|     if(item_normarea > BIG_ITEM_TRESHOLD) { | ||||
|     if(isBig(item.area()) || spatindex.empty()) { | ||||
|         // This branch is for the bigger items..
 | ||||
|         // Here we will use the closest point of the item bounding box to
 | ||||
|         // the already arranged pile. So not the bb center nor the a choosen
 | ||||
|         // corner but whichever is the closest to the center. This will
 | ||||
|         // prevent some unwanted strange arrangements.
 | ||||
| 
 | ||||
|         auto minc = ibb.minCorner(); // bottom left corner
 | ||||
|         auto maxc = ibb.maxCorner(); // top right corner
 | ||||
|  | @ -190,48 +184,68 @@ objfunc(const PointImpl& bincenter, | |||
| 
 | ||||
|         // The smalles distance from the arranged pile center:
 | ||||
|         auto dist = *(std::min_element(dists.begin(), dists.end())) / norm; | ||||
|         auto bindist = pl::distance(ibb.center(), bincenter) / norm; | ||||
|         dist = 0.8*dist + 0.2*bindist; | ||||
| 
 | ||||
|         // Density is the pack density: how big is the arranged pile
 | ||||
|         auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; | ||||
|         double density = 0; | ||||
| 
 | ||||
|         // Prepare a variable for the alignment score.
 | ||||
|         // This will indicate: how well is the candidate item aligned with
 | ||||
|         // its neighbors. We will check the aligment with all neighbors and
 | ||||
|         // return the score for the best alignment. So it is enough for the
 | ||||
|         // candidate to be aligned with only one item.
 | ||||
|         auto alignment_score = std::numeric_limits<double>::max(); | ||||
|         if(remaining.empty()) { | ||||
| 
 | ||||
|         auto& trsh =  item.transformedShape(); | ||||
|             auto mp = merged_pile; | ||||
|             mp.emplace_back(item.transformedShape()); | ||||
|             auto chull = sl::convexHull(mp); | ||||
| 
 | ||||
|         auto querybb = item.boundingBox(); | ||||
|             placers::EdgeCache<PolygonImpl> ec(chull); | ||||
| 
 | ||||
|         // Query the spatial index for the neigbours
 | ||||
|         std::vector<SpatElement> result; | ||||
|         spatindex.query(bgi::intersects(querybb), std::back_inserter(result)); | ||||
|             double circ = ec.circumference() / norm; | ||||
|             double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm; | ||||
|             score = 0.5*circ + 0.5*bcirc; | ||||
| 
 | ||||
|         for(auto& e : result) { // now get the score for the best alignment
 | ||||
|             auto idx = e.second; | ||||
|             auto& p = pile[idx]; | ||||
|             auto parea = areacache[idx]; | ||||
|             auto bb = sl::boundingBox(sl::Shapes<PolygonImpl>{p, trsh}); | ||||
|             auto bbarea = bb.area(); | ||||
|             auto ascore = 1.0 - (item.area() + parea)/bbarea; | ||||
|         } else { | ||||
|             // Prepare a variable for the alignment score.
 | ||||
|             // This will indicate: how well is the candidate item aligned with
 | ||||
|             // its neighbors. We will check the alignment with all neighbors and
 | ||||
|             // return the score for the best alignment. So it is enough for the
 | ||||
|             // candidate to be aligned with only one item.
 | ||||
|             auto alignment_score = 1.0; | ||||
| 
 | ||||
|             if(ascore < alignment_score) alignment_score = ascore; | ||||
|             density = std::sqrt((fullbb.width() / norm )* | ||||
|                                 (fullbb.height() / norm)); | ||||
|             auto querybb = item.boundingBox(); | ||||
| 
 | ||||
|             // Query the spatial index for the neighbors
 | ||||
|             std::vector<SpatElement> result; | ||||
|             result.reserve(spatindex.size()); | ||||
|             if(isBig(item.area())) { | ||||
|                 spatindex.query(bgi::intersects(querybb), | ||||
|                                 std::back_inserter(result)); | ||||
|             } else { | ||||
|                 smalls_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.40 * dist + 0.40 * density + 0.2 * alignment_score; | ||||
|         } | ||||
| 
 | ||||
|         // The final mix of the score is the balance between the distance
 | ||||
|         // from the full pile center, the pack density and the
 | ||||
|         // alignment with the neigbours
 | ||||
|         auto C = 0.33; | ||||
|         score = C * dist +  C * density + C * alignment_score; | ||||
| 
 | ||||
|     } else if( item_normarea < BIG_ITEM_TRESHOLD && spatindex.empty()) { | ||||
|         // If there are no big items, only small, we should consider the
 | ||||
|         // density here as well to not get silly results
 | ||||
|         auto bindist = pl::distance(ibb.center(), bincenter) / norm; | ||||
|         auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; | ||||
|         score = ROUNDNESS_RATIO * bindist + DENSITY_RATIO * density; | ||||
|     } else { | ||||
|         // Here there are the small items that should be placed around the
 | ||||
|         // already processed bigger items.
 | ||||
|  | @ -259,7 +273,9 @@ void fillConfig(PConf& pcfg) { | |||
| 
 | ||||
|     // The accuracy of optimization.
 | ||||
|     // Goes from 0.0 to 1.0 and scales performance as well
 | ||||
|     pcfg.accuracy = 0.6f; | ||||
|     pcfg.accuracy = 0.65f; | ||||
| 
 | ||||
|     pcfg.parallel = true; | ||||
| } | ||||
| 
 | ||||
| template<class TBin> | ||||
|  | @ -268,31 +284,65 @@ class AutoArranger {}; | |||
| template<class TBin> | ||||
| class _ArrBase { | ||||
| protected: | ||||
|     using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>; | ||||
| 
 | ||||
|     using Placer = TPacker<TBin>; | ||||
|     using Selector = FirstFitSelection; | ||||
|     using Packer = Arranger<Placer, Selector>; | ||||
|     using Packer = Nester<Placer, Selector>; | ||||
|     using PConfig = typename Packer::PlacementConfig; | ||||
|     using Distance = TCoord<PointImpl>; | ||||
|     using Pile = ShapeLike::Shapes<PolygonImpl>; | ||||
|     using Pile = sl::Shapes<PolygonImpl>; | ||||
| 
 | ||||
|     Packer pck_; | ||||
|     PConfig pconf_; // Placement configuration
 | ||||
|     double bin_area_; | ||||
|     std::vector<double> areacache_; | ||||
|     SpatIndex rtree_; | ||||
|     SpatIndex smallsrtree_; | ||||
|     double norm_; | ||||
|     Pile merged_pile_; | ||||
|     Box pilebb_; | ||||
|     ItemGroup remaining_; | ||||
|     ItemGroup items_; | ||||
| public: | ||||
| 
 | ||||
|     _ArrBase(const TBin& bin, Distance dist, | ||||
|              std::function<void(unsigned)> progressind): | ||||
|        pck_(bin, dist), bin_area_(ShapeLike::area<PolygonImpl>(bin)) | ||||
|        pck_(bin, dist), bin_area_(sl::area(bin)), | ||||
|        norm_(std::sqrt(sl::area(bin))) | ||||
|     { | ||||
|         fillConfig(pconf_); | ||||
| 
 | ||||
|         pconf_.before_packing = | ||||
|         [this](const Pile& merged_pile,            // merged pile
 | ||||
|                const ItemGroup& items,             // packed items
 | ||||
|                const ItemGroup& remaining)         // future items to be packed
 | ||||
|         { | ||||
|             items_ = items; | ||||
|             merged_pile_ = merged_pile; | ||||
|             remaining_ = remaining; | ||||
| 
 | ||||
|             pilebb_ = sl::boundingBox(merged_pile); | ||||
| 
 | ||||
|             rtree_.clear(); | ||||
|             smallsrtree_.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}); | ||||
|                 smallsrtree_.insert({itm.boundingBox(), idx}); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         pck_.progressIndicator(progressind); | ||||
|     } | ||||
| 
 | ||||
|     template<class...Args> inline IndexedPackGroup operator()(Args&&...args) { | ||||
|         areacache_.clear(); | ||||
|         return pck_.arrangeIndexed(std::forward<Args>(args)...); | ||||
|         rtree_.clear(); | ||||
|         return pck_.executeIndexed(std::forward<Args>(args)...); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  | @ -304,22 +354,71 @@ public: | |||
|                  std::function<void(unsigned)> progressind): | ||||
|         _ArrBase<Box>(bin, dist, progressind) | ||||
|     { | ||||
|         pconf_.object_function = [this, bin] ( | ||||
|                     Pile& pile, | ||||
|                     const Item &item, | ||||
|                     double pile_area, | ||||
|                     double norm, | ||||
|                     double /*penality*/) { | ||||
| 
 | ||||
|             auto result = objfunc(bin.center(), bin_area_, pile, | ||||
|                                   pile_area, item, norm, areacache_, rtree_); | ||||
|         pconf_.object_function = [this, bin] (const Item &item) { | ||||
| 
 | ||||
|             auto result = objfunc(bin.center(), | ||||
|                                   merged_pile_, | ||||
|                                   pilebb_, | ||||
|                                   items_, | ||||
|                                   item, | ||||
|                                   bin_area_, | ||||
|                                   norm_, | ||||
|                                   rtree_, | ||||
|                                   smallsrtree_, | ||||
|                                   remaining_); | ||||
| 
 | ||||
|             double score = std::get<0>(result); | ||||
|             auto& fullbb = std::get<1>(result); | ||||
| 
 | ||||
|             auto wdiff = fullbb.width() - bin.width(); | ||||
|             auto hdiff = fullbb.height() - bin.height(); | ||||
|             if(wdiff > 0) score += std::pow(wdiff, 2) / norm; | ||||
|             if(hdiff > 0) score += std::pow(hdiff, 2) / norm; | ||||
|             double miss = Placer::overfit(fullbb, bin); | ||||
|             miss = miss > 0? miss : 0; | ||||
|             score += miss*miss; | ||||
| 
 | ||||
|             return score; | ||||
|         }; | ||||
| 
 | ||||
|         pck_.configure(pconf_); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| using lnCircle = libnest2d::_Circle<libnest2d::PointImpl>; | ||||
| 
 | ||||
| template<> | ||||
| class AutoArranger<lnCircle>: public _ArrBase<lnCircle> { | ||||
| public: | ||||
| 
 | ||||
|     AutoArranger(const lnCircle& bin, Distance dist, | ||||
|                  std::function<void(unsigned)> progressind): | ||||
|         _ArrBase<lnCircle>(bin, dist, progressind) { | ||||
| 
 | ||||
|         pconf_.object_function = [this, &bin] (const Item &item) { | ||||
| 
 | ||||
|             auto result = objfunc(bin.center(), | ||||
|                                   merged_pile_, | ||||
|                                   pilebb_, | ||||
|                                   items_, | ||||
|                                   item, | ||||
|                                   bin_area_, | ||||
|                                   norm_, | ||||
|                                   rtree_, | ||||
|                                   smallsrtree_, | ||||
|                                   remaining_); | ||||
| 
 | ||||
|             double score = std::get<0>(result); | ||||
| 
 | ||||
|             auto isBig = [this](const Item& itm) { | ||||
|                 return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ; | ||||
|             }; | ||||
| 
 | ||||
|             if(isBig(item)) { | ||||
|                 auto mp = merged_pile_; | ||||
|                 mp.push_back(item.transformedShape()); | ||||
|                 auto chull = sl::convexHull(mp); | ||||
|                 double miss = Placer::overfit(chull, bin); | ||||
|                 if(miss < 0) miss = 0; | ||||
|                 score += miss*miss; | ||||
|             } | ||||
| 
 | ||||
|             return score; | ||||
|         }; | ||||
|  | @ -335,27 +434,21 @@ public: | |||
|                  std::function<void(unsigned)> progressind): | ||||
|         _ArrBase<PolygonImpl>(bin, dist, progressind) | ||||
|     { | ||||
|         pconf_.object_function = [this, &bin] ( | ||||
|                     Pile& pile, | ||||
|                     const Item &item, | ||||
|                     double pile_area, | ||||
|                     double norm, | ||||
|                     double /*penality*/) { | ||||
|         pconf_.object_function = [this, &bin] (const Item &item) { | ||||
| 
 | ||||
|             auto binbb = ShapeLike::boundingBox(bin); | ||||
|             auto result = objfunc(binbb.center(), bin_area_, pile, | ||||
|                                   pile_area, item, norm, areacache_, rtree_); | ||||
|             auto binbb = sl::boundingBox(bin); | ||||
|             auto result = objfunc(binbb.center(), | ||||
|                                   merged_pile_, | ||||
|                                   pilebb_, | ||||
|                                   items_, | ||||
|                                   item, | ||||
|                                   bin_area_, | ||||
|                                   norm_, | ||||
|                                   rtree_, | ||||
|                                   smallsrtree_, | ||||
|                                   remaining_); | ||||
|             double score = std::get<0>(result); | ||||
| 
 | ||||
|             pile.emplace_back(item.transformedShape()); | ||||
|             auto chull = ShapeLike::convexHull(pile); | ||||
|             pile.pop_back(); | ||||
| 
 | ||||
|             // If it does not fit into the print bed we will beat it with a
 | ||||
|             // large penality. If we would not do this, there would be only one
 | ||||
|             // big pile that doesn't care whether it fits onto the print bed.
 | ||||
|             if(!Placer::wouldFit(chull, bin)) score += norm; | ||||
| 
 | ||||
|             return score; | ||||
|         }; | ||||
| 
 | ||||
|  | @ -370,15 +463,18 @@ public: | |||
|     AutoArranger(Distance dist, std::function<void(unsigned)> progressind): | ||||
|         _ArrBase<Box>(Box(0, 0), dist, progressind) | ||||
|     { | ||||
|         this->pconf_.object_function = [this] ( | ||||
|                     Pile& pile, | ||||
|                     const Item &item, | ||||
|                     double pile_area, | ||||
|                     double norm, | ||||
|                     double /*penality*/) { | ||||
|         this->pconf_.object_function = [this] (const Item &item) { | ||||
| 
 | ||||
|             auto result = objfunc({0, 0}, 0, pile, pile_area, | ||||
|                                   item, norm, areacache_, rtree_); | ||||
|             auto result = objfunc({0, 0}, | ||||
|                                   merged_pile_, | ||||
|                                   pilebb_, | ||||
|                                   items_, | ||||
|                                   item, | ||||
|                                   0, | ||||
|                                   norm_, | ||||
|                                   rtree_, | ||||
|                                   smallsrtree_, | ||||
|                                   remaining_); | ||||
|             return std::get<0>(result); | ||||
|         }; | ||||
| 
 | ||||
|  | @ -440,16 +536,104 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| enum BedShapeHint { | ||||
| class Circle { | ||||
|     Point center_; | ||||
|     double radius_; | ||||
| public: | ||||
| 
 | ||||
|     inline Circle(): center_(0, 0), radius_(std::nan("")) {} | ||||
|     inline Circle(const Point& c, double r): center_(c), radius_(r) {} | ||||
| 
 | ||||
|     inline double radius() const { return radius_; } | ||||
|     inline const Point& center() const { return center_; } | ||||
|     inline operator bool() { return !std::isnan(radius_); } | ||||
| }; | ||||
| 
 | ||||
| enum class BedShapeType { | ||||
|     BOX, | ||||
|     CIRCLE, | ||||
|     IRREGULAR, | ||||
|     WHO_KNOWS | ||||
| }; | ||||
| 
 | ||||
| BedShapeHint bedShape(const Slic3r::Polyline& /*bed*/) { | ||||
| struct BedShapeHint { | ||||
|     BedShapeType type; | ||||
|     /*union*/ struct {  // I know but who cares...
 | ||||
|         Circle circ; | ||||
|         BoundingBox box; | ||||
|         Polyline polygon; | ||||
|     } shape; | ||||
| }; | ||||
| 
 | ||||
| BedShapeHint bedShape(const Polyline& bed) { | ||||
|     static const double E = 10/SCALING_FACTOR; | ||||
| 
 | ||||
|     BedShapeHint ret; | ||||
| 
 | ||||
|     auto width = [](const BoundingBox& box) { | ||||
|         return box.max.x - box.min.x; | ||||
|     }; | ||||
| 
 | ||||
|     auto height = [](const BoundingBox& box) { | ||||
|         return box.max.y - box.min.y; | ||||
|     }; | ||||
| 
 | ||||
|     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 bb = bed.bounding_box(); | ||||
| 
 | ||||
|     auto isCircle = [bb](const Polyline& polygon) { | ||||
|         auto center = bb.center(); | ||||
|         std::vector<double> vertex_distances; | ||||
|         double avg_dist = 0; | ||||
|         for (auto pt: polygon.points) | ||||
|         { | ||||
|             double distance = center.distance_to(pt); | ||||
|             vertex_distances.push_back(distance); | ||||
|             avg_dist += distance; | ||||
|         } | ||||
| 
 | ||||
|         avg_dist /= vertex_distances.size(); | ||||
| 
 | ||||
|         Circle ret(center, avg_dist); | ||||
|         for (auto el: vertex_distances) | ||||
|         { | ||||
|             if (abs(el - avg_dist) > 10 * SCALED_EPSILON) | ||||
|                 ret = Circle(); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         return ret; | ||||
|     }; | ||||
| 
 | ||||
|     auto parea = poly_area(bed); | ||||
| 
 | ||||
|     if( (1.0 - parea/area(bb)) < 1e-3 ) { | ||||
|         ret.type = BedShapeType::BOX; | ||||
|         ret.shape.box = bb; | ||||
|     } | ||||
|     else if(auto c = isCircle(bed)) { | ||||
|         ret.type = BedShapeType::CIRCLE; | ||||
|         ret.shape.circ = c; | ||||
|     } else { | ||||
|         ret.type = BedShapeType::IRREGULAR; | ||||
|         ret.shape.polygon = bed; | ||||
|     } | ||||
| 
 | ||||
|     // Determine the bed shape by hand
 | ||||
|     return BOX; | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void applyResult( | ||||
|  | @ -525,7 +709,10 @@ bool arrange(Model &model, coordf_t min_obj_distance, | |||
|     }); | ||||
| 
 | ||||
|     IndexedPackGroup result; | ||||
|     BoundingBox bbb(bed.points); | ||||
| 
 | ||||
|     if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed); | ||||
| 
 | ||||
|     BoundingBox bbb(bed); | ||||
| 
 | ||||
|     auto binbb = Box({ | ||||
|                          static_cast<libnest2d::Coord>(bbb.min.x), | ||||
|  | @ -536,8 +723,8 @@ bool arrange(Model &model, coordf_t min_obj_distance, | |||
|                          static_cast<libnest2d::Coord>(bbb.max.y) | ||||
|                      }); | ||||
| 
 | ||||
|     switch(bedhint) { | ||||
|     case BOX: { | ||||
|     switch(bedhint.type) { | ||||
|     case BedShapeType::BOX: { | ||||
| 
 | ||||
|         // Create the arranger for the box shaped bed
 | ||||
|         AutoArranger<Box> arrange(binbb, min_obj_distance, progressind); | ||||
|  | @ -547,16 +734,22 @@ bool arrange(Model &model, coordf_t min_obj_distance, | |||
|         result = arrange(shapes.begin(), shapes.end()); | ||||
|         break; | ||||
|     } | ||||
|     case CIRCLE: | ||||
|     case BedShapeType::CIRCLE: { | ||||
| 
 | ||||
|         auto c = bedhint.shape.circ; | ||||
|         auto cc = lnCircle({c.center().x, c.center().y} , c.radius()); | ||||
| 
 | ||||
|         AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind); | ||||
|         result = arrange(shapes.begin(), shapes.end()); | ||||
|         break; | ||||
|     case IRREGULAR: | ||||
|     case WHO_KNOWS: { | ||||
|     } | ||||
|     case BedShapeType::IRREGULAR: | ||||
|     case BedShapeType::WHO_KNOWS: { | ||||
| 
 | ||||
|         using P = libnest2d::PolygonImpl; | ||||
| 
 | ||||
|         auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); | ||||
|         P irrbed = ShapeLike::create<PolygonImpl>(std::move(ctour)); | ||||
| 
 | ||||
| //        std::cout << ShapeLike::toString(irrbed) << std::endl;
 | ||||
|         P irrbed = sl::create<PolygonImpl>(std::move(ctour)); | ||||
| 
 | ||||
|         AutoArranger<P> arrange(irrbed, min_obj_distance, progressind); | ||||
| 
 | ||||
|  | @ -567,6 +760,8 @@ bool arrange(Model &model, coordf_t min_obj_distance, | |||
|     } | ||||
|     }; | ||||
| 
 | ||||
|     if(result.empty()) return false; | ||||
| 
 | ||||
|     if(first_bin_only) { | ||||
|         applyResult(result.front(), 0, shapemap); | ||||
|     } else { | ||||
|  |  | |||
|  | @ -43,15 +43,6 @@ namespace GUI { | |||
| PresetBundle* get_preset_bundle(); | ||||
| } | ||||
| 
 | ||||
| static const PrintObjectStep STEP_SLICE                 = posSlice; | ||||
| static const PrintObjectStep STEP_PERIMETERS            = posPerimeters; | ||||
| static const PrintObjectStep STEP_PREPARE_INFILL        = posPrepareInfill; | ||||
| static const PrintObjectStep STEP_INFILL                = posInfill; | ||||
| static const PrintObjectStep STEP_SUPPORTMATERIAL       = posSupportMaterial; | ||||
| static const PrintStep STEP_SKIRT                       = psSkirt; | ||||
| static const PrintStep STEP_BRIM                        = psBrim; | ||||
| static const PrintStep STEP_WIPE_TOWER                  = psWipeTower; | ||||
| 
 | ||||
| AppControllerBoilerplate::ProgresIndicatorPtr | ||||
| AppControllerBoilerplate::global_progress_indicator() { | ||||
|     ProgresIndicatorPtr ret; | ||||
|  | @ -71,193 +62,8 @@ void AppControllerBoilerplate::global_progress_indicator( | |||
|     pri_data_->m.unlock(); | ||||
| } | ||||
| 
 | ||||
| void PrintController::make_skirt() | ||||
| { | ||||
|     assert(print_ != nullptr); | ||||
| 
 | ||||
|     // prerequisites
 | ||||
|     for(auto obj : print_->objects) make_perimeters(obj); | ||||
|     for(auto obj : print_->objects) infill(obj); | ||||
|     for(auto obj : print_->objects) gen_support_material(obj); | ||||
| 
 | ||||
|     if(!print_->state.is_done(STEP_SKIRT)) { | ||||
|         print_->state.set_started(STEP_SKIRT); | ||||
|         print_->skirt.clear(); | ||||
|         if(print_->has_skirt()) print_->_make_skirt(); | ||||
| 
 | ||||
|         print_->state.set_done(STEP_SKIRT); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PrintController::make_brim() | ||||
| { | ||||
|     assert(print_ != nullptr); | ||||
| 
 | ||||
|     // prerequisites
 | ||||
|     for(auto obj : print_->objects) make_perimeters(obj); | ||||
|     for(auto obj : print_->objects) infill(obj); | ||||
|     for(auto obj : print_->objects) gen_support_material(obj); | ||||
|     make_skirt(); | ||||
| 
 | ||||
|     if(!print_->state.is_done(STEP_BRIM)) { | ||||
|         print_->state.set_started(STEP_BRIM); | ||||
| 
 | ||||
|         // since this method must be idempotent, we clear brim paths *before*
 | ||||
|         // checking whether we need to generate them
 | ||||
|         print_->brim.clear(); | ||||
| 
 | ||||
|         if(print_->config.brim_width > 0) print_->_make_brim(); | ||||
| 
 | ||||
|         print_->state.set_done(STEP_BRIM); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PrintController::make_wipe_tower() | ||||
| { | ||||
|     assert(print_ != nullptr); | ||||
| 
 | ||||
|     // prerequisites
 | ||||
|     for(auto obj : print_->objects) make_perimeters(obj); | ||||
|     for(auto obj : print_->objects) infill(obj); | ||||
|     for(auto obj : print_->objects) gen_support_material(obj); | ||||
|     make_skirt(); | ||||
|     make_brim(); | ||||
| 
 | ||||
|     if(!print_->state.is_done(STEP_WIPE_TOWER)) { | ||||
|         print_->state.set_started(STEP_WIPE_TOWER); | ||||
| 
 | ||||
|         // since this method must be idempotent, we clear brim paths *before*
 | ||||
|         // checking whether we need to generate them
 | ||||
|         print_->brim.clear(); | ||||
| 
 | ||||
|         if(print_->has_wipe_tower()) print_->_make_wipe_tower(); | ||||
| 
 | ||||
|         print_->state.set_done(STEP_WIPE_TOWER); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PrintController::slice(PrintObject *pobj) | ||||
| { | ||||
|     assert(pobj != nullptr && print_ != nullptr); | ||||
| 
 | ||||
|     if(pobj->state.is_done(STEP_SLICE)) return; | ||||
| 
 | ||||
|     pobj->state.set_started(STEP_SLICE); | ||||
| 
 | ||||
|     pobj->_slice(); | ||||
| 
 | ||||
|     auto msg = pobj->_fix_slicing_errors(); | ||||
|     if(!msg.empty()) report_issue(IssueType::WARN, msg); | ||||
| 
 | ||||
|     // simplify slices if required
 | ||||
|     if (print_->config.resolution) | ||||
|         pobj->_simplify_slices(scale_(print_->config.resolution)); | ||||
| 
 | ||||
| 
 | ||||
|     if(pobj->layers.empty()) | ||||
|         report_issue(IssueType::ERR, | ||||
|                      _(L("No layers were detected. You might want to repair your " | ||||
|                      "STL file(s) or check their size or thickness and retry")) | ||||
|                      ); | ||||
| 
 | ||||
|     pobj->state.set_done(STEP_SLICE); | ||||
| } | ||||
| 
 | ||||
| void PrintController::make_perimeters(PrintObject *pobj) | ||||
| { | ||||
|     assert(pobj != nullptr); | ||||
| 
 | ||||
|     slice(pobj); | ||||
| 
 | ||||
|     if (!pobj->state.is_done(STEP_PERIMETERS)) { | ||||
|         pobj->_make_perimeters(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PrintController::infill(PrintObject *pobj) | ||||
| { | ||||
|     assert(pobj != nullptr); | ||||
| 
 | ||||
|     make_perimeters(pobj); | ||||
| 
 | ||||
|     if (!pobj->state.is_done(STEP_PREPARE_INFILL)) { | ||||
|         pobj->state.set_started(STEP_PREPARE_INFILL); | ||||
| 
 | ||||
|         pobj->_prepare_infill(); | ||||
| 
 | ||||
|         pobj->state.set_done(STEP_PREPARE_INFILL); | ||||
|     } | ||||
| 
 | ||||
|     pobj->_infill(); | ||||
| } | ||||
| 
 | ||||
| void PrintController::gen_support_material(PrintObject *pobj) | ||||
| { | ||||
|     assert(pobj != nullptr); | ||||
| 
 | ||||
|     // prerequisites
 | ||||
|     slice(pobj); | ||||
| 
 | ||||
|     if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) { | ||||
|         pobj->state.set_started(STEP_SUPPORTMATERIAL); | ||||
| 
 | ||||
|         pobj->clear_support_layers(); | ||||
| 
 | ||||
|         if((pobj->config.support_material || pobj->config.raft_layers > 0) | ||||
|                 && pobj->layers.size() > 1) { | ||||
|             pobj->_generate_support_material(); | ||||
|         } | ||||
| 
 | ||||
|         pobj->state.set_done(STEP_SUPPORTMATERIAL); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri) | ||||
| { | ||||
|     auto st = pri->state(); | ||||
| 
 | ||||
|     Slic3r::trace(3, "Starting the slicing process."); | ||||
| 
 | ||||
|     pri->update(st+20, _(L("Generating perimeters"))); | ||||
|     for(auto obj : print_->objects) make_perimeters(obj); | ||||
| 
 | ||||
|     pri->update(st+60, _(L("Infilling layers"))); | ||||
|     for(auto obj : print_->objects) infill(obj); | ||||
| 
 | ||||
|     pri->update(st+70, _(L("Generating support material"))); | ||||
|     for(auto obj : print_->objects) gen_support_material(obj); | ||||
| 
 | ||||
|     pri->message_fmt(_(L("Weight: %.1fg, Cost: %.1f")), | ||||
|                      print_->total_weight, print_->total_cost); | ||||
|     pri->state(st+85); | ||||
| 
 | ||||
| 
 | ||||
|     pri->update(st+88, _(L("Generating skirt"))); | ||||
|     make_skirt(); | ||||
| 
 | ||||
| 
 | ||||
|     pri->update(st+90, _(L("Generating brim"))); | ||||
|     make_brim(); | ||||
| 
 | ||||
|     pri->update(st+95, _(L("Generating wipe tower"))); | ||||
|     make_wipe_tower(); | ||||
| 
 | ||||
|     pri->update(st+100, _(L("Done"))); | ||||
| 
 | ||||
|     // time to make some statistics..
 | ||||
| 
 | ||||
|     Slic3r::trace(3, _(L("Slicing process finished."))); | ||||
| } | ||||
| 
 | ||||
| void PrintController::slice() | ||||
| { | ||||
|     auto pri = global_progress_indicator(); | ||||
|     if(!pri) pri = create_progress_indicator(100, L("Slicing")); | ||||
|     slice(pri); | ||||
| } | ||||
| 
 | ||||
| void IProgressIndicator::message_fmt( | ||||
|         const string &fmtstr, ...) { | ||||
| void ProgressIndicator::message_fmt( | ||||
|         const std::string &fmtstr, ...) { | ||||
|     std::stringstream ss; | ||||
|     va_list args; | ||||
|     va_start(args, fmtstr); | ||||
|  | @ -321,30 +127,34 @@ void AppController::arrange_model() | |||
|         for(auto& v : bedpoints) | ||||
|             bed.append(Point::new_scale(v.x, v.y)); | ||||
| 
 | ||||
|         if(pind) pind->update(0, _(L("Arranging objects..."))); | ||||
|         if(pind) pind->update(0, L("Arranging objects...")); | ||||
| 
 | ||||
|         try { | ||||
|             arr::BedShapeHint hint; | ||||
|             // TODO: from Sasha from GUI
 | ||||
|             hint.type = arr::BedShapeType::WHO_KNOWS; | ||||
| 
 | ||||
|             arr::arrange(*model_, | ||||
|                          min_obj_distance, | ||||
|                          bed, | ||||
|                          arr::BOX, | ||||
|                          hint, | ||||
|                          false, // create many piles not just one pile
 | ||||
|                          [pind, count](unsigned rem) { | ||||
|                 if(pind) | ||||
|                     pind->update(count - rem, _(L("Arranging objects..."))); | ||||
|                     pind->update(count - rem, L("Arranging objects...")); | ||||
|             }); | ||||
|         } catch(std::exception& e) { | ||||
|             std::cerr << e.what() << std::endl; | ||||
|             report_issue(IssueType::ERR, | ||||
|                          _(L("Could not arrange model objects! " | ||||
|                          "Some geometries may be invalid.")), | ||||
|                          _(L("Exception occurred"))); | ||||
|                          L("Could not arrange model objects! " | ||||
|                          "Some geometries may be invalid."), | ||||
|                          L("Exception occurred")); | ||||
|         } | ||||
| 
 | ||||
|         // Restore previous max value
 | ||||
|         if(pind) { | ||||
|             pind->max(pmax); | ||||
|             pind->update(0, _(L("Arranging done."))); | ||||
|             pind->update(0, L("Arranging done.")); | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| #include <atomic> | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include "IProgressIndicator.hpp" | ||||
| #include "ProgressIndicator.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -15,7 +15,8 @@ class Model; | |||
| class Print; | ||||
| class PrintObject; | ||||
| class PrintConfig; | ||||
| 
 | ||||
| class ProgressStatusBar; | ||||
| class DynamicPrintConfig; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief A boilerplate class for creating application logic. It should provide | ||||
|  | @ -33,7 +34,7 @@ class AppControllerBoilerplate { | |||
| public: | ||||
| 
 | ||||
|     /// A Progress indicator object smart pointer
 | ||||
|     using ProgresIndicatorPtr = std::shared_ptr<IProgressIndicator>; | ||||
|     using ProgresIndicatorPtr = std::shared_ptr<ProgressIndicator>; | ||||
| 
 | ||||
| private: | ||||
|     class PriData;   // Some structure to store progress indication data
 | ||||
|  | @ -46,7 +47,7 @@ public: | |||
|     AppControllerBoilerplate(); | ||||
|     ~AppControllerBoilerplate(); | ||||
| 
 | ||||
|     using Path = string; | ||||
|     using Path = std::string; | ||||
|     using PathList = std::vector<Path>; | ||||
| 
 | ||||
|     /// Common runtime issue types
 | ||||
|  | @ -67,20 +68,20 @@ public: | |||
|      * @return Returns a list of paths choosed by the user. | ||||
|      */ | ||||
|     PathList query_destination_paths( | ||||
|             const string& title, | ||||
|             const std::string& title, | ||||
|             const std::string& extensions) const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Same as query_destination_paths but works for directories only. | ||||
|      */ | ||||
|     PathList query_destination_dirs( | ||||
|             const string& title) const; | ||||
|             const std::string& title) const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Same as query_destination_paths but returns only one path. | ||||
|      */ | ||||
|     Path query_destination_path( | ||||
|             const string& title, | ||||
|             const std::string& title, | ||||
|             const std::string& extensions, | ||||
|             const std::string& hint = "") const; | ||||
| 
 | ||||
|  | @ -95,11 +96,11 @@ public: | |||
|      * title. | ||||
|      */ | ||||
|     bool report_issue(IssueType issuetype, | ||||
|                       const string& description, | ||||
|                       const string& brief); | ||||
|                       const std::string& description, | ||||
|                       const std::string& brief); | ||||
| 
 | ||||
|     bool report_issue(IssueType issuetype, | ||||
|                       const string& description); | ||||
|                       const std::string& description); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Return the global progress indicator for the current controller. | ||||
|  | @ -150,12 +151,12 @@ protected: | |||
|      */ | ||||
|     ProgresIndicatorPtr create_progress_indicator( | ||||
|             unsigned statenum, | ||||
|             const string& title, | ||||
|             const string& firstmsg) const; | ||||
|             const std::string& title, | ||||
|             const std::string& firstmsg) const; | ||||
| 
 | ||||
|     ProgresIndicatorPtr create_progress_indicator( | ||||
|             unsigned statenum, | ||||
|             const string& title) const; | ||||
|             const std::string& title) const; | ||||
| 
 | ||||
|     // This is a global progress indicator placeholder. In the Slic3r UI it can
 | ||||
|     // contain the progress indicator on the statusbar.
 | ||||
|  | @ -167,24 +168,6 @@ protected: | |||
|  */ | ||||
| class PrintController: public AppControllerBoilerplate { | ||||
|     Print *print_ = nullptr; | ||||
| protected: | ||||
| 
 | ||||
|     void make_skirt(); | ||||
|     void make_brim(); | ||||
|     void make_wipe_tower(); | ||||
| 
 | ||||
|     void make_perimeters(PrintObject *pobj); | ||||
|     void infill(PrintObject *pobj); | ||||
|     void gen_support_material(PrintObject *pobj); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Slice one pront object. | ||||
|      * @param pobj The print object. | ||||
|      */ | ||||
|     void slice(PrintObject *pobj); | ||||
| 
 | ||||
|     void slice(ProgresIndicatorPtr pri); | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     // Must be public for perl to use it
 | ||||
|  | @ -199,11 +182,6 @@ public: | |||
|         return PrintController::Ptr( new PrintController(print) ); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Slice the loaded print scene. | ||||
|      */ | ||||
|     void slice(); | ||||
| 
 | ||||
|     const PrintConfig& config() const; | ||||
| }; | ||||
| 
 | ||||
|  | @ -248,7 +226,7 @@ public: | |||
|      * In perl we have a progress indicating status bar on the bottom of the | ||||
|      * window which is defined and created in perl. We can pass the ID-s of the | ||||
|      * gauge and the statusbar id and make a wrapper implementation of the | ||||
|      * IProgressIndicator interface so we can use this GUI widget from C++. | ||||
|      * ProgressIndicator interface so we can use this GUI widget from C++. | ||||
|      * | ||||
|      * This function should be called from perl. | ||||
|      * | ||||
|  |  | |||
|  | @ -32,11 +32,11 @@ void AppControllerBoilerplate::process_events() | |||
| 
 | ||||
| AppControllerBoilerplate::PathList | ||||
| AppControllerBoilerplate::query_destination_paths( | ||||
|         const string &title, | ||||
|         const std::string &title, | ||||
|         const std::string &extensions) const | ||||
| { | ||||
| 
 | ||||
|     wxFileDialog dlg(wxTheApp->GetTopWindow(), title ); | ||||
|     wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) ); | ||||
|     dlg.SetWildcard(extensions); | ||||
| 
 | ||||
|     dlg.ShowModal(); | ||||
|  | @ -52,11 +52,11 @@ AppControllerBoilerplate::query_destination_paths( | |||
| 
 | ||||
| AppControllerBoilerplate::Path | ||||
| AppControllerBoilerplate::query_destination_path( | ||||
|         const string &title, | ||||
|         const std::string &title, | ||||
|         const std::string &extensions, | ||||
|         const std::string& hint) const | ||||
| { | ||||
|     wxFileDialog dlg(wxTheApp->GetTopWindow(), title ); | ||||
|     wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) ); | ||||
|     dlg.SetWildcard(extensions); | ||||
| 
 | ||||
|     dlg.SetFilename(hint); | ||||
|  | @ -71,8 +71,8 @@ AppControllerBoilerplate::query_destination_path( | |||
| } | ||||
| 
 | ||||
| bool AppControllerBoilerplate::report_issue(IssueType issuetype, | ||||
|                                  const string &description, | ||||
|                                  const string &brief) | ||||
|                                  const std::string &description, | ||||
|                                  const std::string &brief) | ||||
| { | ||||
|     auto icon = wxICON_INFORMATION; | ||||
|     auto style = wxOK|wxCENTRE; | ||||
|  | @ -84,15 +84,15 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype, | |||
|     case IssueType::FATAL:  icon = wxICON_ERROR; | ||||
|     } | ||||
| 
 | ||||
|     auto ret = wxMessageBox(description, brief, icon | style); | ||||
|     auto ret = wxMessageBox(_(description), _(brief), icon | style); | ||||
|     return ret != wxCANCEL; | ||||
| } | ||||
| 
 | ||||
| bool AppControllerBoilerplate::report_issue( | ||||
|         AppControllerBoilerplate::IssueType issuetype, | ||||
|         const string &description) | ||||
|         const std::string &description) | ||||
| { | ||||
|     return report_issue(issuetype, description, string()); | ||||
|     return report_issue(issuetype, description, std::string()); | ||||
| } | ||||
| 
 | ||||
| wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent); | ||||
|  | @ -104,10 +104,10 @@ namespace  { | |||
|  * the main thread as well. | ||||
|  */ | ||||
| class GuiProgressIndicator: | ||||
|         public IProgressIndicator, public wxEvtHandler { | ||||
|         public ProgressIndicator, public wxEvtHandler { | ||||
| 
 | ||||
|     wxProgressDialog gauge_; | ||||
|     using Base = IProgressIndicator; | ||||
|     using Base = ProgressIndicator; | ||||
|     wxString message_; | ||||
|     int range_; wxString title_; | ||||
|     bool is_asynch_ = false; | ||||
|  | @ -136,8 +136,8 @@ public: | |||
|     /// Get the mode of parallel operation.
 | ||||
|     inline bool asynch() const { return is_asynch_; } | ||||
| 
 | ||||
|     inline GuiProgressIndicator(int range, const string& title, | ||||
|                                 const string& firstmsg) : | ||||
|     inline GuiProgressIndicator(int range, const wxString& title, | ||||
|                                 const wxString& firstmsg) : | ||||
|         gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(), | ||||
|                wxPD_APP_MODAL | wxPD_AUTO_HIDE), | ||||
|         message_(firstmsg), | ||||
|  | @ -151,11 +151,6 @@ public: | |||
|              this, id_); | ||||
|     } | ||||
| 
 | ||||
|     virtual void cancel() override { | ||||
|         update(max(), "Abort"); | ||||
|         IProgressIndicator::cancel(); | ||||
|     } | ||||
| 
 | ||||
|     virtual void state(float val) override { | ||||
|         state(static_cast<unsigned>(val)); | ||||
|     } | ||||
|  | @ -170,26 +165,28 @@ public: | |||
|         } else _state(st); | ||||
|     } | ||||
| 
 | ||||
|     virtual void message(const string & msg) override { | ||||
|         message_ = msg; | ||||
|     virtual void message(const std::string & msg) override { | ||||
|         message_ = _(msg); | ||||
|     } | ||||
| 
 | ||||
|     virtual void messageFmt(const string& fmt, ...) { | ||||
|     virtual void messageFmt(const std::string& fmt, ...) { | ||||
|         va_list arglist; | ||||
|         va_start(arglist, fmt); | ||||
|         message_ = wxString::Format(wxString(fmt), arglist); | ||||
|         message_ = wxString::Format(_(fmt), arglist); | ||||
|         va_end(arglist); | ||||
|     } | ||||
| 
 | ||||
|     virtual void title(const string & title) override { | ||||
|         title_ = title; | ||||
|     virtual void title(const std::string & title) override { | ||||
|         title_ = _(title); | ||||
|     } | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| AppControllerBoilerplate::ProgresIndicatorPtr | ||||
| AppControllerBoilerplate::create_progress_indicator( | ||||
|         unsigned statenum, const string& title, const string& firstmsg) const | ||||
|         unsigned statenum, | ||||
|         const std::string& title, | ||||
|         const std::string& firstmsg) const | ||||
| { | ||||
|     auto pri = | ||||
|             std::make_shared<GuiProgressIndicator>(statenum, title, firstmsg); | ||||
|  | @ -202,20 +199,20 @@ AppControllerBoilerplate::create_progress_indicator( | |||
| } | ||||
| 
 | ||||
| AppControllerBoilerplate::ProgresIndicatorPtr | ||||
| AppControllerBoilerplate::create_progress_indicator(unsigned statenum, | ||||
|                                                     const string &title) const | ||||
| AppControllerBoilerplate::create_progress_indicator( | ||||
|         unsigned statenum, const std::string &title) const | ||||
| { | ||||
|     return create_progress_indicator(statenum, title, string()); | ||||
|     return create_progress_indicator(statenum, title, std::string()); | ||||
| } | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| // A wrapper progress indicator class around the statusbar created in perl.
 | ||||
| class Wrapper: public IProgressIndicator, public wxEvtHandler { | ||||
| class Wrapper: public ProgressIndicator, public wxEvtHandler { | ||||
|     wxGauge *gauge_; | ||||
|     wxStatusBar *stbar_; | ||||
|     using Base = IProgressIndicator; | ||||
|     std::string message_; | ||||
|     using Base = ProgressIndicator; | ||||
|     wxString message_; | ||||
|     AppControllerBoilerplate& ctl_; | ||||
| 
 | ||||
|     void showProgress(bool show = true) { | ||||
|  | @ -223,7 +220,7 @@ class Wrapper: public IProgressIndicator, public wxEvtHandler { | |||
|     } | ||||
| 
 | ||||
|     void _state(unsigned st) { | ||||
|         if( st <= IProgressIndicator::max() ) { | ||||
|         if( st <= ProgressIndicator::max() ) { | ||||
|             Base::state(st); | ||||
| 
 | ||||
|             if(!gauge_->IsShown()) showProgress(true); | ||||
|  | @ -266,7 +263,7 @@ public: | |||
|     virtual void max(float val) override { | ||||
|         if(val > 1.0) { | ||||
|             gauge_->SetRange(static_cast<int>(val)); | ||||
|             IProgressIndicator::max(val); | ||||
|             ProgressIndicator::max(val); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -280,18 +277,18 @@ public: | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     virtual void message(const string & msg) override { | ||||
|         message_ = msg; | ||||
|     virtual void message(const std::string & msg) override { | ||||
|         message_ = _(msg); | ||||
|     } | ||||
| 
 | ||||
|     virtual void message_fmt(const string& fmt, ...) override { | ||||
|     virtual void message_fmt(const std::string& fmt, ...) override { | ||||
|         va_list arglist; | ||||
|         va_start(arglist, fmt); | ||||
|         message_ = wxString::Format(fmt, arglist); | ||||
|         message_ = wxString::Format(_(fmt), arglist); | ||||
|         va_end(arglist); | ||||
|     } | ||||
| 
 | ||||
|     virtual void title(const string & /*title*/) override {} | ||||
|     virtual void title(const std::string & /*title*/) override {} | ||||
| 
 | ||||
| }; | ||||
| } | ||||
|  |  | |||
|  | @ -3,16 +3,15 @@ | |||
| 
 | ||||
| #include <string> | ||||
| #include <functional> | ||||
| #include "Strings.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Generic progress indication interface. | ||||
|  */ | ||||
| class IProgressIndicator { | ||||
| class ProgressIndicator { | ||||
| public: | ||||
|     using CancelFn = std::function<void(void)>; // Cancel functio signature.
 | ||||
|     using CancelFn = std::function<void(void)>; // Cancel function signature.
 | ||||
| 
 | ||||
| private: | ||||
|     float state_ = .0f, max_ = 1.f, step_; | ||||
|  | @ -20,7 +19,7 @@ private: | |||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     inline virtual ~IProgressIndicator() {} | ||||
|     inline virtual ~ProgressIndicator() {} | ||||
| 
 | ||||
|     /// Get the maximum of the progress range.
 | ||||
|     float max() const { return max_; } | ||||
|  | @ -28,14 +27,14 @@ public: | |||
|     /// Get the current progress state
 | ||||
|     float state() const { return state_; } | ||||
| 
 | ||||
|     /// Set the maximum of hte progress range
 | ||||
|     /// Set the maximum of the progress range
 | ||||
|     virtual void max(float maxval) { max_ = maxval; } | ||||
| 
 | ||||
|     /// Set the current state of the progress.
 | ||||
|     virtual void state(float val)  { state_ = val; } | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Number of states int the progress. Can be used insted of giving a | ||||
|      * @brief Number of states int the progress. Can be used instead of giving a | ||||
|      * maximum value. | ||||
|      */ | ||||
|     virtual void states(unsigned statenum) { | ||||
|  | @ -43,25 +42,19 @@ public: | |||
|     } | ||||
| 
 | ||||
|     /// Message shown on the next status update.
 | ||||
|     virtual void message(const string&) = 0; | ||||
|     virtual void message(const std::string&) = 0; | ||||
| 
 | ||||
|     /// Title of the operaton.
 | ||||
|     virtual void title(const string&) = 0; | ||||
|     /// Title of the operation.
 | ||||
|     virtual void title(const std::string&) = 0; | ||||
| 
 | ||||
|     /// Formatted message for the next status update. Works just like sprinf.
 | ||||
|     virtual void message_fmt(const string& fmt, ...); | ||||
|     /// Formatted message for the next status update. Works just like sprintf.
 | ||||
|     virtual void message_fmt(const std::string& fmt, ...); | ||||
| 
 | ||||
|     /// Set up a cancel callback for the operation if feasible.
 | ||||
|     inline void on_cancel(CancelFn func) { cancelfunc_ = func; } | ||||
|     virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Explicitly shut down the progress indicator and call the associated | ||||
|      * callback. | ||||
|      */ | ||||
|     virtual void cancel() { cancelfunc_(); } | ||||
| 
 | ||||
|     /// Convinience function to call message and status update in one function.
 | ||||
|     void update(float st, const string& msg) { | ||||
|     /// Convenience function to call message and status update in one function.
 | ||||
|     void update(float st, const std::string& msg) { | ||||
|         message(msg); state(st); | ||||
|     } | ||||
| }; | ||||
|  | @ -1,10 +0,0 @@ | |||
| #ifndef STRINGS_HPP | ||||
| #define STRINGS_HPP | ||||
| 
 | ||||
| #include "GUI/GUI.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| using string = wxString; | ||||
| } | ||||
| 
 | ||||
| #endif // STRINGS_HPP
 | ||||
|  | @ -8,10 +8,7 @@ | |||
| %} | ||||
| 
 | ||||
| %name{Slic3r::PrintController} class PrintController { | ||||
| 
 | ||||
|     PrintController(Print *print); | ||||
| 
 | ||||
|     void slice(); | ||||
| }; | ||||
| 
 | ||||
| %name{Slic3r::AppController} class AppController { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bubnikv
						bubnikv