mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 20:51:12 -06:00 
			
		
		
		
	Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_3dconnexion
This commit is contained in:
		
						commit
						9e7e1fb5e2
					
				
					 60 changed files with 3908 additions and 577 deletions
				
			
		|  | @ -256,7 +256,7 @@ if(NOT WIN32) | |||
|     # boost::process was introduced first in version 1.64.0 | ||||
|     set(MINIMUM_BOOST_VERSION "1.64.0") | ||||
| endif() | ||||
| set(_boost_components "system;filesystem;thread;log;locale;regex") | ||||
| set(_boost_components "system;filesystem;thread;log;locale;regex;chrono;atomic;date_time") | ||||
| find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS ${_boost_components}) | ||||
| 
 | ||||
| add_library(boost_libs INTERFACE) | ||||
|  |  | |||
							
								
								
									
										3
									
								
								deps/blosc-mods.patch
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								deps/blosc-mods.patch
									
										
									
									
										vendored
									
									
								
							|  | @ -1,8 +1,9 @@ | |||
| From 5669891dfaaa4c814f3ec667ca6bf4e693aea978 Mon Sep 17 00:00:00 2001 | ||||
| From 7cf6c014a36f1712efbdbe9bc52d2d4922b54673 Mon Sep 17 00:00:00 2001 | ||||
| From: tamasmeszaros <meszaros.q@gmail.com> | ||||
| Date: Wed, 30 Oct 2019 12:54:52 +0100 | ||||
| Subject: [PATCH] Blosc 1.17 fixes and cmake config script | ||||
| 
 | ||||
| Signed-off-by: tamasmeszaros <meszaros.q@gmail.com> | ||||
| ---
 | ||||
|  CMakeLists.txt                   | 105 +++++++++++++++++----------------- | ||||
|  blosc/CMakeLists.txt             | 118 +++++++++------------------------------ | ||||
|  |  | |||
							
								
								
									
										6
									
								
								deps/deps-macos.cmake
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								deps/deps-macos.cmake
									
										
									
									
										vendored
									
									
								
							|  | @ -16,8 +16,8 @@ include("deps-unix-common.cmake") | |||
| 
 | ||||
| ExternalProject_Add(dep_boost | ||||
|     EXCLUDE_FROM_ALL 1 | ||||
|     URL "https://dl.bintray.com/boostorg/release/1.71.0/source/boost_1_71_0.tar.gz" | ||||
|     URL_HASH SHA256=96b34f7468f26a141f6020efb813f1a2f3dfb9797ecf76a7d7cbd843cc95f5bd | ||||
|     URL "https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz" | ||||
|     URL_HASH SHA256=882b48708d211a5f48e60b0124cf5863c1534cd544ecd0664bb534a4b5d506e9 | ||||
|     BUILD_IN_SOURCE 1 | ||||
|     CONFIGURE_COMMAND ./bootstrap.sh | ||||
|         --with-toolset=clang | ||||
|  | @ -90,8 +90,6 @@ ExternalProject_Add(dep_wxwidgets | |||
|     EXCLUDE_FROM_ALL 1 | ||||
|     GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" | ||||
|     GIT_TAG v3.1.1-patched | ||||
| #    URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.2/wxWidgets-3.1.2.tar.bz2" | ||||
| #    URL_HASH SHA256=4cb8d23d70f9261debf7d6cfeca667fc0a7d2b6565adb8f1c484f9b674f1f27a | ||||
|     BUILD_IN_SOURCE 1 | ||||
| #    PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/wxwidgets-pngprefix.h" src/png/pngprefix.h | ||||
|     CONFIGURE_COMMAND env "CXXFLAGS=${DEP_WERRORS_SDK}" "CFLAGS=${DEP_WERRORS_SDK}" ./configure | ||||
|  |  | |||
							
								
								
									
										17
									
								
								deps/deps-unix-common.cmake
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								deps/deps-unix-common.cmake
									
										
									
									
										vendored
									
									
								
							|  | @ -55,14 +55,14 @@ find_package(Git REQUIRED) | |||
| 
 | ||||
| ExternalProject_Add(dep_qhull | ||||
|     EXCLUDE_FROM_ALL 1 | ||||
|     URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" | ||||
|     URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 | ||||
|     #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" | ||||
|     #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 | ||||
|     GIT_REPOSITORY  https://github.com/qhull/qhull.git | ||||
|     GIT_TAG         7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch | ||||
|     CMAKE_ARGS | ||||
|         -DBUILD_SHARED_LIBS=OFF | ||||
|         -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local | ||||
|         ${DEP_CMAKE_OPTS} | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch | ||||
| ) | ||||
| 
 | ||||
| ExternalProject_Add(dep_blosc | ||||
|  | @ -80,8 +80,8 @@ ExternalProject_Add(dep_blosc | |||
|         -DBUILD_TESTS=OFF  | ||||
|         -DBUILD_BENCHMARKS=OFF  | ||||
|         -DPREFER_EXTERNAL_ZLIB=ON | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch | ||||
|     PATCH_COMMAND       ${GIT_EXECUTABLE} reset --hard && git clean -df &&  | ||||
|                         ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch | ||||
| ) | ||||
| 
 | ||||
| ExternalProject_Add(dep_openexr | ||||
|  | @ -96,7 +96,6 @@ ExternalProject_Add(dep_openexr | |||
|         -DPYILMBASE_ENABLE:BOOL=OFF  | ||||
|         -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF | ||||
|         -DOPENEXR_BUILD_UTILS:BOOL=OFF | ||||
|     UPDATE_COMMAND "" | ||||
| ) | ||||
| 
 | ||||
| ExternalProject_Add(dep_openvdb | ||||
|  | @ -116,6 +115,6 @@ ExternalProject_Add(dep_openvdb | |||
|         -DOPENVDB_CORE_STATIC=ON  | ||||
|         -DTBB_STATIC=ON | ||||
|         -DOPENVDB_BUILD_VDB_PRINT=ON | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch | ||||
|     PATCH_COMMAND PATCH_COMMAND     ${GIT_EXECUTABLE} checkout -f -- . && git clean -df &&  | ||||
|                                     ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch | ||||
| ) | ||||
							
								
								
									
										19
									
								
								deps/deps-windows.cmake
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								deps/deps-windows.cmake
									
										
									
									
										vendored
									
									
								
							|  | @ -218,16 +218,16 @@ find_package(Git REQUIRED) | |||
| 
 | ||||
| ExternalProject_Add(dep_qhull | ||||
|     EXCLUDE_FROM_ALL 1 | ||||
|     URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" | ||||
|     URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 | ||||
|     #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" | ||||
|     #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 | ||||
|     GIT_REPOSITORY  https://github.com/qhull/qhull.git | ||||
|     GIT_TAG         7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch | ||||
|     CMAKE_GENERATOR "${DEP_MSVC_GEN}" | ||||
|     CMAKE_ARGS | ||||
|         -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local | ||||
|         -DBUILD_SHARED_LIBS=OFF | ||||
|         -DCMAKE_POSITION_INDEPENDENT_CODE=ON | ||||
|         -DCMAKE_DEBUG_POSTFIX=d | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch | ||||
|     BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj | ||||
|     INSTALL_COMMAND "" | ||||
| ) | ||||
|  | @ -288,8 +288,8 @@ ExternalProject_Add(dep_blosc | |||
|         -DPREFER_EXTERNAL_ZLIB=ON | ||||
|         -DBLOSC_IS_SUBPROJECT:BOOL=ON | ||||
|         -DBLOSC_INSTALL:BOOL=ON | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch | ||||
|     PATCH_COMMAND       ${GIT_EXECUTABLE} checkout -f -- . && git clean -df &&  | ||||
|                         ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch | ||||
|     BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj | ||||
|     INSTALL_COMMAND "" | ||||
| ) | ||||
|  | @ -311,7 +311,6 @@ ExternalProject_Add(dep_openexr | |||
|         -DPYILMBASE_ENABLE:BOOL=OFF  | ||||
|         -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF | ||||
|         -DOPENEXR_BUILD_UTILS:BOOL=OFF | ||||
|     UPDATE_COMMAND "" | ||||
|     BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj | ||||
|     INSTALL_COMMAND "" | ||||
| ) | ||||
|  | @ -324,7 +323,7 @@ ExternalProject_Add(dep_openvdb | |||
|     #URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48 | ||||
|     GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git | ||||
|     GIT_TAG  aebaf8d95be5e57fd33949281ec357db4a576c2e #v6.2.1 | ||||
|     DEPENDS dep_blosc dep_openexr #dep_tbb dep_boost | ||||
|     DEPENDS dep_blosc dep_openexr dep_tbb dep_boost | ||||
|     CMAKE_GENERATOR "${DEP_MSVC_GEN}" | ||||
|     CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" | ||||
|     CMAKE_ARGS | ||||
|  | @ -340,8 +339,8 @@ ExternalProject_Add(dep_openvdb | |||
|         -DTBB_STATIC=ON | ||||
|         -DOPENVDB_BUILD_VDB_PRINT=ON | ||||
|     BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch | ||||
|     PATCH_COMMAND       ${GIT_EXECUTABLE} checkout -f -- . && git clean -df &&  | ||||
|                         ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch | ||||
|     INSTALL_COMMAND "" | ||||
| ) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										3
									
								
								deps/openvdb-mods.patch
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								deps/openvdb-mods.patch
									
										
									
									
										vendored
									
									
								
							|  | @ -1,8 +1,9 @@ | |||
| From e48f4a835fe7cb391f9f90945472bd367fb4c4f1 Mon Sep 17 00:00:00 2001 | ||||
| From dbe038fce8a15ddc9a5c83ec5156d7bc9e178015 Mon Sep 17 00:00:00 2001 | ||||
| From: tamasmeszaros <meszaros.q@gmail.com> | ||||
| Date: Wed, 16 Oct 2019 17:42:50 +0200 | ||||
| Subject: [PATCH] Build fixes for PrusaSlicer integration | ||||
| 
 | ||||
| Signed-off-by: tamasmeszaros <meszaros.q@gmail.com> | ||||
| ---
 | ||||
|  CMakeLists.txt                  |   3 - | ||||
|  cmake/FindBlosc.cmake           | 218 --------------- | ||||
|  |  | |||
							
								
								
									
										47
									
								
								resources/icons/bed/ender3.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								resources/icons/bed/ender3.svg
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										109
									
								
								resources/icons/bed/mini.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								resources/icons/bed/mini.svg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,109 @@ | |||
| <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="180.7mm" height="180.6mm" viewBox="0 0 512.1 512"> | ||||
|   <title>bed_texture_denser</title> | ||||
|   <path d="M510.6,510.9" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <path d="M.4,510.9" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="511.3" y1="511.3" x2="511.3" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <path d="M.4.4" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <path d="M510.6.4" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="1.1" y1="0.8" x2="1.1" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="1.1" y1="511.3" x2="1.1" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="511.3" y1="0.8" x2="511.3" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="1.1" y1="511.3" x2="511.3" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="1.1" y1="0.8" x2="511.3" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <g> | ||||
|     <g> | ||||
|       <line x1="1.1" y1="383.6" x2="3.2" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="5.4" y1="383.6" x2="7" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="8.1" y1="383.6" x2="508.1" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="509.2" y1="383.6" x2="511.3" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|     <g> | ||||
|       <line x1="1.1" y1="256" x2="3.2" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="5.4" y1="256" x2="7" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="8.1" y1="256" x2="508.1" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="509.2" y1="256" x2="511.3" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|     <g> | ||||
|       <line x1="511.3" y1="128.4" x2="509.2" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="507.1" y1="128.4" x2="505.4" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="504.4" y1="128.4" x2="4.3" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="3.2" y1="128.4" x2="1.1" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|     <g> | ||||
|       <line x1="128.7" y1="511.3" x2="128.7" y2="509.1" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="128.7" y1="507" x2="128.7" y2="505.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="128.7" y1="504.3" x2="128.7" y2="3.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.2734222412109375,2.1367111206054688,0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="128.7" y1="2.9" x2="128.7" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|     <g> | ||||
|       <line x1="256.2" y1="0.8" x2="256.2" y2="2.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="256.2" y1="5" x2="256.2" y2="6.7" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="256.2" y1="7.7" x2="256.2" y2="508.1" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.2734222412109375,2.1367111206054688,0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="256.2" y1="509.1" x2="256.2" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|     <g> | ||||
|       <line x1="383.8" y1="482.3" x2="383.8" y2="480.2" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="383.8" y1="478" x2="383.8" y2="476.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5678567290306091,2.129462480545044"/> | ||||
|       <line x1="383.8" y1="475.3" x2="383.8" y2="3.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.258924961090088,2.129462480545044,0.5678567290306091,2.129462480545044"/> | ||||
|       <line x1="383.8" y1="2.9" x2="383.8" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|   </g> | ||||
|   <g> | ||||
|     <path d="M277.3,489.1c4.6,0,7.4,2.8,7.4,8.1s-2.9,8.1-7.4,8.1-7.4-2.9-7.4-8.1S272.9,489.1,277.3,489.1Zm3.7,8.1c0-3.8-1.5-5.7-3.7-5.7s-3.8,1.9-3.8,5.7,1.3,5.6,3.8,5.6S281,500.9,281,497.2Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M293.1,499h-2.5v6h-3.3V489.3h6.1a7.3,7.3,0,0,1,3.2.6,4.1,4.1,0,0,1,2.6,4,4.4,4.4,0,0,1-3.1,4.3h0l3.5,6.8H296Zm-.1-2.4c1.5,0,2.7-.7,2.7-2.5a2.4,2.4,0,0,0-2.7-2.5h-2.4v5Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M302,489.3h3.4V505H302Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M311.6,497.2c0,4,1.4,5.6,3.8,5.6s3.4-1.3,3.6-3.5V499h-3.7v-2.4h6.8V505h-2.6v-2.2h-.1a5,5,0,0,1-4.6,2.5c-4.4,0-6.8-3.1-6.8-7.9s3-8.3,7.4-8.3,6.1,1.7,6.4,4.9h-3.4a2.8,2.8,0,0,0-3-2.5C313,491.5,311.6,493.5,311.6,497.2Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M325.1,489.3h3.4V505h-3.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M331.7,489.3h3.7l5,8.5a16.8,16.8,0,0,1,1.2,2.3h0V489.3h3.1V505h-3.5l-5.3-8.7a12.8,12.8,0,0,1-1.1-2.4h-.1V505h-3Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M356.8,501.5H351l-1.1,3.5h-3.2l5.4-15.7h3.8l5.6,15.7h-3.6Zm-3.5-7.1-1.5,4.6H356l-1.5-4.5c-.4-1.4-.6-2.3-.6-2.3h0A15.3,15.3,0,0,1,353.3,494.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M363.4,489.3h3.4v13h6.8V505H363.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M384,499.6V505h-3.4V489.3h5.5c3.4,0,6,1.4,6,5s-2.8,5.3-6.3,5.3Zm2-2.5a2.5,2.5,0,0,0,2.8-2.7c0-1.9-1.1-2.7-2.8-2.7h-2v5.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M400.1,499h-2.4v6h-3.4V489.3h6.1a7.3,7.3,0,0,1,3.2.6,3.9,3.9,0,0,1,2.6,4,4.5,4.5,0,0,1-3,4.3h0l3.5,6.8H403Zm-.1-2.4c1.5,0,2.8-.7,2.8-2.5a2.4,2.4,0,0,0-2.7-2.5h-2.4v5Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M408.7,489.3H412v9.1a5.6,5.6,0,0,0,.6,3.2,3.6,3.6,0,0,0,5,0c.6-.8.5-2.1.5-3.2v-9.1h3.3v10.2c0,3.9-2.5,5.8-6.4,5.8s-6.3-1.8-6.3-5.8Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M432.4,493.5a2,2,0,0,0-2.3-2c-1.5,0-2.4.8-2.4,1.9s.6,1.7,2.1,1.9l2.4.5c2.8.5,4.1,2,4.1,4.3s-2.3,5.2-6.3,5.2-6.1-1.9-6.2-4.8h3.5a2.5,2.5,0,0,0,2.8,2.3c1.9,0,2.7-.9,2.7-2.1s-.5-1.7-2.2-2l-2.3-.4c-2.4-.5-4.1-1.9-4.1-4.4s2.2-4.8,5.9-4.8,5.7,2,5.8,4.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M447.4,501.5h-5.7l-1.2,3.5h-3.2l5.5-15.7h3.7l5.6,15.7h-3.5Zm-3.5-7.1-1.4,4.6h4.1l-1.4-4.5a11.3,11.3,0,0,1-.6-2.3h-.1A15.3,15.3,0,0,1,443.9,494.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M458.7,489.3h4.9l2.8,8.4a15.7,15.7,0,0,1,.7,2.3h0l.7-2.3,2.8-8.4h4.9V505h-3.3V492.7h-.1a26.9,26.9,0,0,1-1,3.3l-2.9,9h-2.5l-2.9-9a26.9,26.9,0,0,1-1-3.3h0V505h-3.1Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M478.6,489.3H482V505h-3.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M485.2,489.3h3.7l5,8.5a16.8,16.8,0,0,1,1.2,2.3h0V489.3h3.1V505h-3.5l-5.3-8.7a12.8,12.8,0,0,1-1.1-2.4h-.1V505h-3Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M501.3,489.3h3.4V505h-3.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|   </g> | ||||
|   <line x1="0.4" y1="28.7" x2="511" y2="28.7" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.4" y1="57.1" x2="511" y2="57.1" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.4" y1="28.7" x2="511" y2="28.7" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.1" y1="85.4" x2="510.7" y2="85.4" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.1" y1="113.8" x2="510.7" y2="113.8" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.9" y1="142.1" x2="511.5" y2="142.1" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <path d="M509.9,141.7" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <path d="M-.7,141.7" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="1.5" y1="198.8" x2="512.1" y2="198.8" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line y1="170.5" x2="510.6" y2="170.5" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.5" y1="227.2" x2="511.1" y2="227.2" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="283.9" x2="511.2" y2="283.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.5" y1="28.7" x2="511.1" y2="28.7" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="312.2" x2="511.2" y2="312.2" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="340.6" x2="511.2" y2="340.6" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="368.9" x2="511.2" y2="368.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="397.2" x2="511.2" y2="397.2" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="425.6" x2="511.2" y2="425.6" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="453.9" x2="511.2" y2="453.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="482.3" x2="511.2" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <path d="M481.9,511.4" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <path d="M481.9.8" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="454.3" y1="1.2" x2="454.3" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="482.6" y1="1.2" x2="482.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="425.9" y1="0.8" x2="425.9" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="397.6" y1="0.8" x2="397.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="369.2" y1="1.7" x2="369.2" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="312.6" y1="2.2" x2="312.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="340.9" y1="0.8" x2="340.9" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="284.2" y1="1.3" x2="284.2" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="227.5" y1="1.3" x2="227.5" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="482.6" y1="1.3" x2="482.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="199.2" y1="1.3" x2="199.2" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="170.8" y1="1.3" x2="170.8" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="142.5" y1="1.3" x2="142.5" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="114.1" y1="1.3" x2="114.1" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="85.8" y1="1.3" x2="85.8" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="57.4" y1="1.3" x2="57.4" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="29.1" y1="1.3" x2="29.1" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 14 KiB | 
							
								
								
									
										
											BIN
										
									
								
								resources/icons/printers/Creality_Ender3.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/icons/printers/Creality_Ender3.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 61 KiB | 
							
								
								
									
										
											BIN
										
									
								
								resources/icons/printers/PrusaResearch_MINI.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/icons/printers/PrusaResearch_MINI.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 58 KiB | 
							
								
								
									
										
											BIN
										
									
								
								resources/models/ender3_bed.stl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/models/ender3_bed.stl
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								resources/models/mini_bed.stl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/models/mini_bed.stl
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										415
									
								
								resources/profiles/Creality.ini
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										415
									
								
								resources/profiles/Creality.ini
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,415 @@ | |||
| # Print profiles for the Creality printers. | ||||
| 
 | ||||
| [vendor] | ||||
| # Vendor name will be shown by the Config Wizard. | ||||
| name = Creality | ||||
| # Configuration version of this file. Config file will only be installed, if the config_version differs. | ||||
| # This means, the server may force the PrusaSlicer configuration to be downgraded. | ||||
| config_version = 0.0.1 | ||||
| # Where to get the updates from? | ||||
| config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Creality/ | ||||
| # changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1% | ||||
| 
 | ||||
| # The printer models will be shown by the Configuration Wizard in this order, | ||||
| # also the first model installed & the first nozzle installed will be activated after install. | ||||
| # Printer model name will be shown by the installation wizard. | ||||
| 
 | ||||
| [printer_model:ENDER3] | ||||
| name = Creality Ender-3 | ||||
| variants = 0.4 | ||||
| technology = FFF | ||||
| 
 | ||||
| # All presets starting with asterisk, for example *common*, are intermediate and they will | ||||
| # not make it into the user interface. | ||||
| 
 | ||||
| # Common print preset | ||||
| [print:*common*] | ||||
| avoid_crossing_perimeters = 0 | ||||
| bridge_angle = 0 | ||||
| bridge_flow_ratio = 0.95 | ||||
| bridge_speed = 25 | ||||
| brim_width = 0 | ||||
| clip_multipart_objects = 1 | ||||
| compatible_printers =  | ||||
| complete_objects = 0 | ||||
| dont_support_bridges = 1 | ||||
| elefant_foot_compensation = 0 | ||||
| ensure_vertical_shell_thickness = 1 | ||||
| external_fill_pattern = rectilinear | ||||
| external_perimeters_first = 0 | ||||
| external_perimeter_extrusion_width = 0.45 | ||||
| extra_perimeters = 0 | ||||
| extruder_clearance_height = 25 | ||||
| extruder_clearance_radius = 45 | ||||
| extrusion_width = 0.45 | ||||
| fill_angle = 45 | ||||
| fill_density = 20% | ||||
| fill_pattern = grid | ||||
| first_layer_extrusion_width = 0.42 | ||||
| first_layer_height = 0.2 | ||||
| first_layer_speed = 20 | ||||
| gap_fill_speed = 30 | ||||
| gcode_comments = 0 | ||||
| infill_every_layers = 1 | ||||
| infill_extruder = 1 | ||||
| infill_extrusion_width = 0.45 | ||||
| infill_first = 0 | ||||
| infill_only_where_needed = 0 | ||||
| infill_overlap = 25% | ||||
| interface_shells = 0 | ||||
| max_print_speed = 100 | ||||
| max_volumetric_extrusion_rate_slope_negative = 0 | ||||
| max_volumetric_extrusion_rate_slope_positive = 0 | ||||
| max_volumetric_speed = 0 | ||||
| min_skirt_length = 4 | ||||
| notes =  | ||||
| overhangs = 0 | ||||
| only_retract_when_crossing_perimeters = 0 | ||||
| ooze_prevention = 0 | ||||
| output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode | ||||
| perimeters = 2 | ||||
| perimeter_extruder = 1 | ||||
| perimeter_extrusion_width = 0.45 | ||||
| post_process =  | ||||
| print_settings_id =  | ||||
| raft_layers = 0 | ||||
| resolution = 0 | ||||
| seam_position = nearest | ||||
| single_extruder_multi_material_priming = 1 | ||||
| skirts = 1 | ||||
| skirt_distance = 2 | ||||
| skirt_height = 2 | ||||
| small_perimeter_speed = 25 | ||||
| solid_infill_below_area = 0 | ||||
| solid_infill_every_layers = 0 | ||||
| solid_infill_extruder = 1 | ||||
| solid_infill_extrusion_width = 0.45 | ||||
| spiral_vase = 0 | ||||
| standby_temperature_delta = -5 | ||||
| support_material = 0 | ||||
| support_material_extruder = 0 | ||||
| support_material_extrusion_width = 0.4 | ||||
| support_material_interface_extruder = 0 | ||||
| support_material_angle = 0 | ||||
| support_material_buildplate_only = 0 | ||||
| support_material_enforce_layers = 0 | ||||
| support_material_contact_distance = 0.15 | ||||
| support_material_interface_contact_loops = 0 | ||||
| support_material_interface_layers = 2 | ||||
| support_material_interface_spacing = 0.2 | ||||
| support_material_interface_speed = 100% | ||||
| support_material_pattern = rectilinear | ||||
| support_material_spacing = 2 | ||||
| support_material_speed = 40 | ||||
| support_material_synchronize_layers = 0 | ||||
| support_material_threshold = 45 | ||||
| support_material_with_sheath = 0 | ||||
| support_material_xy_spacing = 60% | ||||
| thin_walls = 0 | ||||
| top_infill_extrusion_width = 0.45 | ||||
| top_solid_infill_speed = 40 | ||||
| travel_speed = 100 | ||||
| wipe_tower = 0 | ||||
| wipe_tower_bridging = 10 | ||||
| wipe_tower_rotation_angle = 0 | ||||
| wipe_tower_width = 60 | ||||
| wipe_tower_x = 170 | ||||
| wipe_tower_y = 140 | ||||
| xy_size_compensation = 0 | ||||
| 
 | ||||
| [print:*0.12mm*] | ||||
| inherits = *common* | ||||
| perimeter_speed = 40 | ||||
| external_perimeter_speed = 25 | ||||
| infill_speed = 50 | ||||
| solid_infill_speed = 40 | ||||
| layer_height = 0.12 | ||||
| perimeters = 3 | ||||
| top_infill_extrusion_width = 0.4 | ||||
| bottom_solid_layers = 6 | ||||
| top_solid_layers = 7 | ||||
| 
 | ||||
| [print:*0.20mm*] | ||||
| inherits = *common* | ||||
| perimeter_speed = 40 | ||||
| external_perimeter_speed = 25 | ||||
| infill_speed = 50 | ||||
| solid_infill_speed = 40 | ||||
| layer_height = 0.20 | ||||
| top_infill_extrusion_width = 0.4 | ||||
| bottom_solid_layers = 4 | ||||
| top_solid_layers = 5 | ||||
| 
 | ||||
| [print:*0.24mm*] | ||||
| inherits = *common* | ||||
| perimeter_speed = 40 | ||||
| external_perimeter_speed = 25 | ||||
| infill_speed = 50 | ||||
| solid_infill_speed = 40 | ||||
| layer_height = 0.24 | ||||
| top_infill_extrusion_width = 0.45 | ||||
| bottom_solid_layers = 3 | ||||
| top_solid_layers = 4 | ||||
| 
 | ||||
| [print:0.12mm DETAIL ENDER3] | ||||
| inherits = *0.12mm* | ||||
| alias=0.12mm DETAIL | ||||
| travel_speed = 150 | ||||
| infill_speed = 50 | ||||
| solid_infill_speed = 40 | ||||
| top_solid_infill_speed = 30 | ||||
| support_material_extrusion_width = 0.38 | ||||
| compatible_printers_condition = printer_model=="ENDER3" and nozzle_diameter[0]==0.4 | ||||
| 
 | ||||
| [print:0.20mm NORMAL ENDER3] | ||||
| inherits = *0.20mm* | ||||
| alias=0.20mm NORMAL | ||||
| travel_speed = 150 | ||||
| infill_speed = 50 | ||||
| solid_infill_speed = 40 | ||||
| top_solid_infill_speed = 30 | ||||
| support_material_extrusion_width = 0.38 | ||||
| compatible_printers_condition = printer_model=="ENDER3" and nozzle_diameter[0]==0.4 | ||||
| 
 | ||||
| [print:0.24mm DRAFT ENDER3] | ||||
| inherits = *0.24mm* | ||||
| alias=0.24mm DRAFT | ||||
| travel_speed = 150 | ||||
| infill_speed = 50 | ||||
| solid_infill_speed = 40 | ||||
| top_solid_infill_speed = 30 | ||||
| support_material_extrusion_width = 0.38 | ||||
| compatible_printers_condition = printer_model=="ENDER3" and nozzle_diameter[0]==0.4 | ||||
| 
 | ||||
| # Common filament preset | ||||
| [filament:*common*] | ||||
| cooling = 0 | ||||
| compatible_printers =  | ||||
| compatible_printers_condition =  | ||||
| extrusion_multiplier = 1 | ||||
| filament_cost = 0 | ||||
| filament_density = 0 | ||||
| filament_diameter = 1.75 | ||||
| filament_notes = "" | ||||
| filament_settings_id = "" | ||||
| filament_soluble = 0 | ||||
| min_print_speed = 15 | ||||
| slowdown_below_layer_time = 20 | ||||
| 
 | ||||
| [filament:*PLA*] | ||||
| inherits = *common* | ||||
| bed_temperature = 40 | ||||
| fan_below_layer_time = 100 | ||||
| filament_colour = #FF3232 | ||||
| filament_max_volumetric_speed = 15 | ||||
| filament_type = PLA | ||||
| filament_density = 1.24 | ||||
| filament_cost = 20 | ||||
| first_layer_bed_temperature = 40 | ||||
| first_layer_temperature = 215 | ||||
| fan_always_on = 1 | ||||
| cooling = 1 | ||||
| max_fan_speed = 100 | ||||
| min_fan_speed = 100 | ||||
| bridge_fan_speed = 100 | ||||
| disable_fan_first_layers = 1 | ||||
| temperature = 210 | ||||
| 
 | ||||
| [filament:*PET*] | ||||
| inherits = *common* | ||||
| bed_temperature = 70 | ||||
| cooling = 1 | ||||
| disable_fan_first_layers = 3 | ||||
| fan_below_layer_time = 20 | ||||
| filament_colour = #FF8000 | ||||
| filament_max_volumetric_speed = 8 | ||||
| filament_type = PETG | ||||
| filament_density = 1.27 | ||||
| filament_cost = 30 | ||||
| first_layer_bed_temperature =70 | ||||
| first_layer_temperature = 240 | ||||
| fan_always_on = 1 | ||||
| max_fan_speed = 50 | ||||
| min_fan_speed = 20 | ||||
| bridge_fan_speed = 100 | ||||
| temperature = 240 | ||||
| 
 | ||||
| [filament:*ABS*] | ||||
| inherits = *common* | ||||
| bed_temperature = 100 | ||||
| cooling = 0 | ||||
| disable_fan_first_layers = 3 | ||||
| fan_below_layer_time = 20 | ||||
| filament_colour = #3A80CA | ||||
| filament_max_volumetric_speed = 11 | ||||
| filament_type = PLA | ||||
| filament_density = 1.04 | ||||
| filament_cost = 20 | ||||
| first_layer_bed_temperature = 100 | ||||
| first_layer_temperature = 245 | ||||
| fan_always_on = 0 | ||||
| max_fan_speed = 0 | ||||
| min_fan_speed = 0 | ||||
| bridge_fan_speed = 30 | ||||
| top_fan_speed = 0 | ||||
| temperature = 245 | ||||
| 
 | ||||
| [filament:Generic PLA ENDER3] | ||||
| inherits = *PLA* | ||||
| alias=Generic PLA | ||||
| 
 | ||||
| [filament:Generic PET ENDER3] | ||||
| inherits = *PET* | ||||
| alias=Generic PET | ||||
| 
 | ||||
| [filament:Generic ABS ENDER3] | ||||
| inherits = *ABS* | ||||
| alias=Generic ABS | ||||
| first_layer_bed_temperature = 90		 | ||||
| bed_temperature = 90 | ||||
| 
 | ||||
| [filament:Creality PLA ENDER3] | ||||
| inherits = *PLA* | ||||
| alias=Creality PLA | ||||
| temperature = 205 | ||||
| bed_temperature = 40 | ||||
| first_layer_temperature = 210 | ||||
| first_layer_bed_temperature =40 | ||||
| 
 | ||||
| [filament:Creality PET ENDER3] | ||||
| inherits = *PET* | ||||
| alias=Creality PET | ||||
| temperature = 240 | ||||
| bed_temperature = 70 | ||||
| first_layer_temperature = 240 | ||||
| first_layer_bed_temperature =70 | ||||
| max_fan_speed = 40 | ||||
| min_fan_speed = 20 | ||||
| 
 | ||||
| [filament:Creality ABS ENDER3] | ||||
| inherits = *ABS* | ||||
| alias=Creality ABS | ||||
| temperature = 240 | ||||
| bed_temperature = 90 | ||||
| first_layer_temperature = 240 | ||||
| first_layer_bed_temperature =90 | ||||
| 
 | ||||
| [filament:Prusament PLA ENDER3] | ||||
| inherits = *PLA* | ||||
| alias=Prusament PLA | ||||
| temperature = 215 | ||||
| bed_temperature = 40 | ||||
| first_layer_temperature = 215 | ||||
| first_layer_bed_temperature =40 | ||||
| filament_cost = 24.99 | ||||
| filament_density = 1.24 | ||||
| 
 | ||||
| [filament:Prusament PETG ENDER3] | ||||
| inherits = *PET* | ||||
| alias=Prusament PETG | ||||
| temperature = 245 | ||||
| bed_temperature = 70 | ||||
| first_layer_temperature = 245 | ||||
| first_layer_bed_temperature =70 | ||||
| filament_cost = 24.99 | ||||
| filament_density = 1.27 | ||||
| 
 | ||||
| # Common printer preset | ||||
| [printer:*common*] | ||||
| printer_technology = FFF | ||||
| bed_shape = 0x0,200x0,200x200,0x200 | ||||
| before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n | ||||
| between_objects_gcode =  | ||||
| deretract_speed = 0 | ||||
| extruder_colour = #FFFF00 | ||||
| extruder_offset = 0x0 | ||||
| gcode_flavor = marlin | ||||
| silent_mode = 0 | ||||
| remaining_times = 0 | ||||
| machine_max_acceleration_e = 10000 | ||||
| machine_max_acceleration_extruding = 2000 | ||||
| machine_max_acceleration_retracting = 1500 | ||||
| machine_max_acceleration_x = 3000 | ||||
| machine_max_acceleration_y = 3000 | ||||
| machine_max_acceleration_z = 500 | ||||
| machine_max_feedrate_e = 120 | ||||
| machine_max_feedrate_x = 500 | ||||
| machine_max_feedrate_y = 500 | ||||
| machine_max_feedrate_z = 12 | ||||
| machine_max_jerk_e = 2.5 | ||||
| machine_max_jerk_x = 20 | ||||
| machine_max_jerk_y = 20 | ||||
| machine_max_jerk_z = 0.4 | ||||
| machine_min_extruding_rate = 0 | ||||
| machine_min_travel_rate = 0 | ||||
| layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] | ||||
| max_layer_height = 0.3 | ||||
| min_layer_height = 0.07 | ||||
| max_print_height = 200 | ||||
| nozzle_diameter = 0.4 | ||||
| octoprint_apikey =  | ||||
| octoprint_host =  | ||||
| printer_notes =  | ||||
| printer_settings_id =  | ||||
| retract_before_travel = 1 | ||||
| retract_before_wipe = 0% | ||||
| retract_layer_change = 1 | ||||
| retract_length = 1 | ||||
| retract_length_toolchange = 1 | ||||
| retract_lift = 0 | ||||
| retract_lift_above = 0 | ||||
| retract_lift_below = 0 | ||||
| retract_restart_extra = 0 | ||||
| retract_restart_extra_toolchange = 0 | ||||
| retract_speed = 35 | ||||
| serial_port =  | ||||
| serial_speed = 250000 | ||||
| single_extruder_multi_material = 0 | ||||
| start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 ; home all\nG92 E0.0\nG1 Z0.15 F240\nG1 X60.0 E9.0  F800.0 ; intro line\nG1 X100.0 E12.5 F800 ; intro line\nG92 E0.0 | ||||
| end_gcode = M104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 F3000 ; home X axis\nM84 ; disable motors | ||||
| toolchange_gcode =  | ||||
| use_firmware_retraction = 0 | ||||
| use_relative_e_distances = 1 | ||||
| use_volumetric_e = 0 | ||||
| variable_layer_height = 1 | ||||
| wipe = 1 | ||||
| z_offset = 0 | ||||
| printer_model =  | ||||
| default_print_profile =  | ||||
| default_filament_profile =  | ||||
| 
 | ||||
| [printer:Creality ENDER-3] | ||||
| inherits = *common* | ||||
| printer_model = ENDER3 | ||||
| printer_variant = 0.4 | ||||
| max_layer_height = 0.25 | ||||
| min_layer_height = 0.1 | ||||
| bed_shape = 0x0,220x0,220x220,0x220 | ||||
| max_print_height = 250 | ||||
| machine_max_acceleration_e = 5000 | ||||
| machine_max_acceleration_extruding = 500 | ||||
| machine_max_acceleration_retracting = 1000 | ||||
| machine_max_acceleration_x = 500 | ||||
| machine_max_acceleration_y = 500 | ||||
| machine_max_acceleration_z = 100 | ||||
| machine_max_feedrate_e = 60 | ||||
| machine_max_feedrate_x = 500 | ||||
| machine_max_feedrate_y = 500 | ||||
| machine_max_feedrate_z = 10 | ||||
| machine_max_jerk_e = 5 | ||||
| machine_max_jerk_x = 8 | ||||
| machine_max_jerk_y = 8 | ||||
| machine_max_jerk_z = 0.4 | ||||
| machine_min_extruding_rate = 0 | ||||
| machine_min_travel_rate = 0 | ||||
| printer_notes =  | ||||
| nozzle_diameter = 0.4 | ||||
| retract_before_travel = 2 | ||||
| retract_length = 5 | ||||
| retract_speed = 60 | ||||
| deretract_speed = 40 | ||||
| retract_before_wipe = 70% | ||||
| default_print_profile = 0.20mm NORMAL | ||||
| default_filament_profile = Creality PLA | ||||
| start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 ; home all\nG1 Z2 F240\nG1 X2 Y10 F3000\nG1 Z0.28 F240\nG92 E0.0\nG1 Y190 E15.0 F1500.0 ; intro line\nG1 X2.3 F5000\nG1 Y10 E30 F1200.0 ; intro line\nG92 E0.0 | ||||
| end_gcode = M104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+10, max_print_height)} F600{endif} ; Move print head up\nG1 X0 Y200 F3000 ; present print\nM84 X Y E ; disable motors | ||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1205,7 +1205,7 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s | |||
| { | ||||
| #ifndef NDEBUG | ||||
| 	// Verify that the deltas are all non positive.
 | ||||
| for (const std::vector<float>& ds : deltas) | ||||
| 	for (const std::vector<float>& ds : deltas) | ||||
| 		for (float delta : ds) | ||||
| 			assert(delta <= 0.); | ||||
| 	assert(expoly.holes.size() + 1 == deltas.size()); | ||||
|  |  | |||
|  | @ -46,11 +46,29 @@ void EdgeGrid::Grid::create(const Polygons &polygons, coord_t resolution) | |||
| 			++ ncontours; | ||||
| 
 | ||||
| 	// Collect the contours.
 | ||||
| 	m_contours.assign(ncontours, NULL); | ||||
| 	m_contours.assign(ncontours, nullptr); | ||||
| 	ncontours = 0; | ||||
| 	for (size_t j = 0; j < polygons.size(); ++ j) | ||||
| 		if (! polygons[j].points.empty()) | ||||
| 			m_contours[ncontours++] = &polygons[j].points; | ||||
| 			m_contours[ncontours ++] = &polygons[j].points; | ||||
| 
 | ||||
| 	create_from_m_contours(resolution); | ||||
| } | ||||
| 
 | ||||
| void EdgeGrid::Grid::create(const std::vector<Points> &polygons, coord_t resolution) | ||||
| { | ||||
| 	// Count the contours.
 | ||||
| 	size_t ncontours = 0; | ||||
| 	for (size_t j = 0; j < polygons.size(); ++ j) | ||||
| 		if (! polygons[j].empty()) | ||||
| 			++ ncontours; | ||||
| 
 | ||||
| 	// Collect the contours.
 | ||||
| 	m_contours.assign(ncontours, nullptr); | ||||
| 	ncontours = 0; | ||||
| 	for (size_t j = 0; j < polygons.size(); ++ j) | ||||
| 		if (! polygons[j].empty()) | ||||
| 			m_contours[ncontours ++] = &polygons[j]; | ||||
| 
 | ||||
| 	create_from_m_contours(resolution); | ||||
| } | ||||
|  | @ -66,7 +84,7 @@ void EdgeGrid::Grid::create(const ExPolygon &expoly, coord_t resolution) | |||
| 			++ ncontours; | ||||
| 
 | ||||
| 	// Collect the contours.
 | ||||
| 	m_contours.assign(ncontours, NULL); | ||||
| 	m_contours.assign(ncontours, nullptr); | ||||
| 	ncontours = 0; | ||||
| 	if (! expoly.contour.points.empty()) | ||||
| 		m_contours[ncontours++] = &expoly.contour.points; | ||||
|  | @ -91,7 +109,7 @@ void EdgeGrid::Grid::create(const ExPolygons &expolygons, coord_t resolution) | |||
| 	} | ||||
| 
 | ||||
| 	// Collect the contours.
 | ||||
| 	m_contours.assign(ncontours, NULL); | ||||
| 	m_contours.assign(ncontours, nullptr); | ||||
| 	ncontours = 0; | ||||
| 	for (size_t i = 0; i < expolygons.size(); ++ i) { | ||||
| 		const ExPolygon &expoly = expolygons[i]; | ||||
|  | @ -1122,7 +1140,7 @@ EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt | |||
| 						Vec2d vfoot = foot - pt.cast<double>(); | ||||
| 						double dist_foot = vfoot.norm(); | ||||
| 						double dist_foot_err = dist_foot - d_min; | ||||
| 						assert(std::abs(dist_foot_err) < 1e-7 * d_min); | ||||
| 						assert(std::abs(dist_foot_err) < 1e-7 || std::abs(dist_foot_err) < 1e-7 * d_min); | ||||
| #endif /* NDEBUG */ | ||||
| 					} | ||||
| 				} | ||||
|  | @ -1145,7 +1163,7 @@ EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt | |||
| 				vfoot = p1.cast<double>() * (1. - result.t) + p2.cast<double>() * result.t - pt.cast<double>(); | ||||
| 			double dist_foot = vfoot.norm(); | ||||
| 			double dist_foot_err = dist_foot - std::abs(result.distance); | ||||
| 			assert(std::abs(dist_foot_err) < 1e-7 * std::abs(result.distance)); | ||||
| 			assert(std::abs(dist_foot_err) < 1e-7 || std::abs(dist_foot_err) < 1e-7 * std::abs(result.distance)); | ||||
| 		} | ||||
| #endif /* NDEBUG */ | ||||
| 	} else | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ public: | |||
| 	void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; } | ||||
| 
 | ||||
| 	void create(const Polygons &polygons, coord_t resolution); | ||||
| 	void create(const std::vector<Points> &polygons, coord_t resolution); | ||||
| 	void create(const ExPolygon &expoly, coord_t resolution); | ||||
| 	void create(const ExPolygons &expolygons, coord_t resolution); | ||||
| 	void create(const ExPolygonCollection &expolygons, coord_t resolution); | ||||
|  | @ -208,6 +209,25 @@ public: | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	template<typename VISITOR> void visit_cells_intersecting_box(BoundingBox bbox, VISITOR &visitor) const | ||||
| 	{ | ||||
| 		// End points of the line segment.
 | ||||
| 		bbox.min -= m_bbox.min; | ||||
| 		bbox.max -= m_bbox.min + Point(1, 1); | ||||
| 		// Get the cells of the end points.
 | ||||
| 		bbox.min /= m_resolution; | ||||
| 		bbox.max /= m_resolution; | ||||
| 		// Trim with the cells.
 | ||||
| 		bbox.min.x() = std::max(bbox.min.x(), 0); | ||||
| 		bbox.min.y() = std::max(bbox.min.y(), 0); | ||||
| 		bbox.max.x() = std::min(bbox.max.x(), (coord_t)m_cols - 1); | ||||
| 		bbox.max.y() = std::min(bbox.max.y(), (coord_t)m_rows - 1); | ||||
| 		for (coord_t iy = bbox.min.y(); iy <= bbox.max.y(); ++ iy) | ||||
| 			for (coord_t ix = bbox.min.x(); ix <= bbox.max.x(); ++ ix) | ||||
| 				if (! visitor(iy, ix)) | ||||
| 					return; | ||||
| 	} | ||||
| 
 | ||||
| 	std::pair<std::vector<std::pair<size_t, size_t>>::const_iterator, std::vector<std::pair<size_t, size_t>>::const_iterator> cell_data_range(coord_t row, coord_t col) const | ||||
| 	{ | ||||
| 		const EdgeGrid::Grid::Cell &cell = m_cells[row * m_cols + col]; | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #include "Flow.hpp" | ||||
| #include "Geometry.hpp" | ||||
| #include "SVG.hpp" | ||||
| #include "Utils.hpp" | ||||
| 
 | ||||
| #include <cmath> | ||||
| #include <cassert> | ||||
|  | @ -26,6 +27,10 @@ struct ResampledPoint { | |||
| 	double		curve_parameter; | ||||
| }; | ||||
| 
 | ||||
| // Distance calculated using SDF (Shape Diameter Function).
 | ||||
| // The distance is calculated by casting a fan of rays and measuring the intersection distance.
 | ||||
| // Thus the calculation is relatively slow. For the Elephant foot compensation purpose, this distance metric does not avoid 
 | ||||
| // pinching off small pieces of a contour, thus this function has been superseded by contour_distance2().
 | ||||
| std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const size_t idx_contour, const Slic3r::Points &contour, const std::vector<ResampledPoint> &resampled_point_parameters, double search_radius) | ||||
| { | ||||
| 	assert(! contour.empty()); | ||||
|  | @ -60,9 +65,9 @@ std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const size_t idx | |||
| 				for (size_t axis = 0; axis < 2; ++ axis) { | ||||
| 					double dx = std::abs(dir(axis)); | ||||
| 					if (dx >= EPSILON) { | ||||
| 						double tedge = (dir(axis) > 0) ? (double(bbox.max(axis)) - EPSILON - this->pt(axis)) : (this->pt(axis) - double(bbox.min(axis)) - EPSILON); | ||||
| 						double tedge = (dir(axis) > 0) ? (double(bbox.max(axis)) - SCALED_EPSILON - this->pt(axis)) : (this->pt(axis) - double(bbox.min(axis)) - SCALED_EPSILON); | ||||
| 						if (tedge < dx) | ||||
| 							t = tedge / dx; | ||||
| 							t = std::min(t, tedge / dx); | ||||
| 					} | ||||
| 				} | ||||
| 				this->dir      = dir; | ||||
|  | @ -70,6 +75,7 @@ std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const size_t idx | |||
| 					dir *= t; | ||||
| 				this->pt_end   = (this->pt + dir).cast<coord_t>(); | ||||
| 				this->t_min    = 1.; | ||||
| 				assert(this->grid.bbox().contains(this->pt_start) && this->grid.bbox().contains(this->pt_end)); | ||||
| 			} | ||||
| 
 | ||||
| 			bool operator()(coord_t iy, coord_t ix) { | ||||
|  | @ -166,7 +172,7 @@ std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const size_t idx | |||
| 			SVG svg(debug_out_path("contour_distance_raycasted-%d-%d.svg", iRun, &pt_next - contour.data()).c_str(), bbox); | ||||
| 			svg.draw(expoly_grid); | ||||
| 			svg.draw_outline(Polygon(contour), "blue", scale_(0.01)); | ||||
| 			svg.draw(*pt_this, "red", scale_(0.1)); | ||||
| 			svg.draw(*pt_this, "red", coord_t(scale_(0.1))); | ||||
| #endif /* CONTOUR_DISTANCE_DEBUG_SVG */ | ||||
| 
 | ||||
| 			for (int i = - num_rays + 1; i < num_rays; ++ i) { | ||||
|  | @ -181,7 +187,7 @@ std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const size_t idx | |||
| 				svg.draw(Line(visitor.pt_start, visitor.pt_end), "yellow", scale_(0.01)); | ||||
| 				if (visitor.t_min < 1.) { | ||||
| 					Vec2d pt = visitor.pt + visitor.dir * visitor.t_min; | ||||
| 					svg.draw(Point(pt), "red", scale_(0.1)); | ||||
| 					svg.draw(Point(pt), "red", coord_t(scale_(0.1))); | ||||
| 				} | ||||
| #endif /* CONTOUR_DISTANCE_DEBUG_SVG */ | ||||
| 			} | ||||
|  | @ -208,7 +214,7 @@ std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const size_t idx | |||
| 			out.emplace_back(float(distances.front() * search_radius)); | ||||
| #endif | ||||
| #ifdef CONTOUR_DISTANCE_DEBUG_SVG | ||||
| 			printf("contour_distance_raycasted-%d-%d.svg - distance %lf\n", iRun, &pt_next - contour.data(), unscale<double>(out.back())); | ||||
| 			printf("contour_distance_raycasted-%d-%d.svg - distance %lf\n", iRun, int(&pt_next - contour.data()), unscale<double>(out.back())); | ||||
| #endif /* CONTOUR_DISTANCE_DEBUG_SVG */ | ||||
| 			pt_this = &pt_next; | ||||
| 			idx_pt_this = &pt_next - contour.data(); | ||||
|  | @ -222,6 +228,188 @@ std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const size_t idx | |||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| // Contour distance by measuring the closest point of an ExPolygon stored inside the EdgeGrid, while filtering out points of the same contour
 | ||||
| // at concave regions, or convex regions with low curvature (curvature is estimated as a ratio between contour length and chordal distance crossing the contour ends).
 | ||||
| std::vector<float> contour_distance2(const EdgeGrid::Grid &grid, const size_t idx_contour, const Slic3r::Points &contour, const std::vector<ResampledPoint> &resampled_point_parameters, double compensation, double search_radius) | ||||
| { | ||||
| 	assert(! contour.empty()); | ||||
| 	assert(contour.size() >= 2); | ||||
| 
 | ||||
| 	std::vector<float> out; | ||||
| 
 | ||||
| 	if (contour.size() > 2)  | ||||
| 	{ | ||||
| #ifdef CONTOUR_DISTANCE_DEBUG_SVG | ||||
| 		static int iRun = 0; | ||||
| 		++ iRun; | ||||
| 		BoundingBox bbox = get_extents(contour); | ||||
| 		bbox.merge(grid.bbox()); | ||||
| 		ExPolygon expoly_grid; | ||||
| 		expoly_grid.contour = Polygon(*grid.contours().front()); | ||||
| 		for (size_t i = 1; i < grid.contours().size(); ++ i) | ||||
| 			expoly_grid.holes.emplace_back(Polygon(*grid.contours()[i])); | ||||
| #endif | ||||
| 		struct Visitor { | ||||
| 			Visitor(const EdgeGrid::Grid &grid, const size_t idx_contour, const std::vector<ResampledPoint> &resampled_point_parameters, double dist_same_contour_accept, double dist_same_contour_reject) : | ||||
| 				grid(grid), idx_contour(idx_contour), contour(*grid.contours()[idx_contour]), resampled_point_parameters(resampled_point_parameters), dist_same_contour_accept(dist_same_contour_accept), dist_same_contour_reject(dist_same_contour_reject) {} | ||||
| 
 | ||||
| 			void init(const Points &contour, const Point &apoint) { | ||||
| 				this->idx_point = &apoint - contour.data(); | ||||
| 				this->point 	= apoint; | ||||
| 				this->found     = false; | ||||
| 				this->dir_inside = this->dir_inside_at_point(contour, this->idx_point); | ||||
| 			} | ||||
| 
 | ||||
| 			bool operator()(coord_t iy, coord_t ix) { | ||||
| 				// Called with a row and colum of the grid cell, which is intersected by a line.
 | ||||
| 				auto cell_data_range = this->grid.cell_data_range(iy, ix); | ||||
| 				for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) { | ||||
| 					// End points of the line segment and their vector.
 | ||||
| 					std::pair<const Point&, const Point&> segment = this->grid.segment(*it_contour_and_segment); | ||||
| 				    const Vec2d   v  = (segment.second - segment.first).cast<double>(); | ||||
| 				    const Vec2d   va = (this->point - segment.first).cast<double>(); | ||||
| 				    const double  l2 = v.squaredNorm();  // avoid a sqrt
 | ||||
| 				    const double  t  = (l2 == 0.0) ? 0. : clamp(0., 1., va.dot(v) / l2); | ||||
| 				    // Closest point from this->point to the segment.
 | ||||
| 				    const Vec2d   foot = segment.first.cast<double>() + t * v; | ||||
| 					const Vec2d   bisector = foot - this->point.cast<double>(); | ||||
| 					const double  dist = bisector.norm(); | ||||
| 				    if ((! this->found || dist < this->distance) && this->dir_inside.dot(bisector) > 0) { | ||||
| 						bool	accept = true; | ||||
| 					    if (it_contour_and_segment->first == idx_contour) { | ||||
| 							// Complex case: The closest segment originates from the same contour as the starting point.
 | ||||
| 							// Reject the closest point if its distance along the contour is reasonable compared to the current contour bisector (this->pt, foot).
 | ||||
| 							double param_lo = resampled_point_parameters[this->idx_point].curve_parameter; | ||||
| 							double param_hi; | ||||
| 							double param_end = resampled_point_parameters.back().curve_parameter; | ||||
| 							const Slic3r::Points &ipts = *grid.contours()[it_contour_and_segment->first]; | ||||
| 							const size_t		  ipt  = it_contour_and_segment->second; | ||||
| 							{ | ||||
| 								ResampledPoint key(ipt, false, 0.); | ||||
| 								auto lower = [](const ResampledPoint& l, const ResampledPoint r) { return l.idx_src < r.idx_src || (l.idx_src == r.idx_src && int(l.interpolated) > int(r.interpolated)); }; | ||||
| 								auto it = std::lower_bound(resampled_point_parameters.begin(), resampled_point_parameters.end(), key, lower); | ||||
| 								assert(it != resampled_point_parameters.end() && it->idx_src == ipt && ! it->interpolated); | ||||
| 								param_hi = t * sqrt(l2); | ||||
| 								if (ipt + 1 < ipts.size()) | ||||
| 									param_hi += it->curve_parameter; | ||||
| 							} | ||||
| 							if (param_lo > param_hi) | ||||
| 								std::swap(param_lo, param_hi); | ||||
| 							assert(param_lo > - SCALED_EPSILON && param_lo <= param_end + SCALED_EPSILON); | ||||
| 							assert(param_hi > - SCALED_EPSILON && param_hi <= param_end + SCALED_EPSILON); | ||||
| 							double dist_along_contour = std::min(param_hi - param_lo, param_lo + param_end - param_hi); | ||||
| 							if (dist_along_contour < dist_same_contour_accept) | ||||
| 								accept = false; | ||||
| 							else if (dist < dist_same_contour_reject + SCALED_EPSILON) { | ||||
| 								// this->point is close to foot. This point will only be accepted if the path along the contour is significantly 
 | ||||
| 								// longer than the bisector. That is, the path shall not bulge away from the bisector too much.
 | ||||
| 								// Bulge is estimated by 0.6 of the circle circumference drawn around the bisector.
 | ||||
| 								// Test whether the contour is convex or concave.
 | ||||
| 								bool inside =  | ||||
| 									(t == 0.) ? this->inside_corner(ipts, ipt, this->point) : | ||||
| 									(t == 1.) ? this->inside_corner(ipts, ipt + 1 == ipts.size() ? 0 : ipt + 1, this->point) : | ||||
| 									this->left_of_segment(ipts, ipt, this->point); | ||||
| 								accept = inside && dist_along_contour > 0.6 * M_PI * dist; | ||||
| 							} | ||||
| 					    } | ||||
| 					    if (accept && (! this->found || dist < this->distance)) { | ||||
| 					    	// Simple case: Just measure the shortest distance.
 | ||||
| 							this->distance = dist; | ||||
| #ifdef CONTOUR_DISTANCE_DEBUG_SVG | ||||
| 							this->closest_point = foot.cast<coord_t>(); | ||||
| #endif /* CONTOUR_DISTANCE_DEBUG_SVG */ | ||||
| 							this->found    = true; | ||||
| 					    } | ||||
| 					} | ||||
| 				} | ||||
| 				// Continue traversing the grid.
 | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			const EdgeGrid::Grid 			   &grid; | ||||
| 			const size_t 		  				idx_contour; | ||||
| 			const Points					   &contour; | ||||
| 			const std::vector<ResampledPoint>  &resampled_point_parameters; | ||||
| 			const double                        dist_same_contour_accept; | ||||
| 			const double 						dist_same_contour_reject; | ||||
| 
 | ||||
| 			size_t 								idx_point; | ||||
| 			Point			      				point; | ||||
| 			// Direction inside the contour from idx_point, not normalized.
 | ||||
| 			Vec2d								dir_inside; | ||||
| 			bool 								found; | ||||
| 			double 								distance; | ||||
| #ifdef CONTOUR_DISTANCE_DEBUG_SVG | ||||
| 			Point 								closest_point; | ||||
| #endif /* CONTOUR_DISTANCE_DEBUG_SVG */ | ||||
| 
 | ||||
| 		private: | ||||
| 			static Vec2d dir_inside_at_point(const Points &contour, size_t i) { | ||||
| 				size_t iprev = prev_idx_modulo(i, contour); | ||||
| 				size_t inext = next_idx_modulo(i, contour); | ||||
| 				Vec2d v1 = (contour[i] - contour[iprev]).cast<double>(); | ||||
| 				Vec2d v2 = (contour[inext] - contour[i]).cast<double>(); | ||||
| 				return Vec2d(- v1.y() - v2.y(), v1.x() + v2.x()); | ||||
| 			} | ||||
| 			static Vec2d dir_inside_at_segment(const Points& contour, size_t i) { | ||||
| 				size_t inext = next_idx_modulo(i, contour); | ||||
| 				Vec2d v = (contour[inext] - contour[i]).cast<double>(); | ||||
| 				return Vec2d(- v.y(), v.x()); | ||||
| 			} | ||||
| 
 | ||||
| 			static bool inside_corner(const Slic3r::Points &contour, size_t i, const Point &pt_oposite) { | ||||
| 				const Vec2d pt = pt_oposite.cast<double>(); | ||||
| 				size_t iprev = prev_idx_modulo(i, contour); | ||||
| 				size_t inext = next_idx_modulo(i, contour); | ||||
| 				Vec2d v1 = (contour[i] - contour[iprev]).cast<double>(); | ||||
| 				Vec2d v2 = (contour[inext] - contour[i]).cast<double>(); | ||||
| 				bool  left_of_v1 = cross2(v1, pt - contour[iprev].cast<double>()) > 0.; | ||||
| 				bool  left_of_v2 = cross2(v2, pt - contour[i    ].cast<double>()) > 0.; | ||||
| 				return cross2(v1, v2) > 0 ?  | ||||
| 					left_of_v1 && left_of_v2 : // convex corner
 | ||||
| 					left_of_v1 || left_of_v2;  // concave corner
 | ||||
| 			} | ||||
| 			static bool left_of_segment(const Slic3r::Points &contour, size_t i, const Point &pt_oposite) { | ||||
| 				const Vec2d pt = pt_oposite.cast<double>(); | ||||
| 				size_t inext = next_idx_modulo(i, contour); | ||||
| 				Vec2d v = (contour[inext] - contour[i]).cast<double>(); | ||||
| 				return cross2(v, pt - contour[i].cast<double>()) > 0.; | ||||
| 			} | ||||
| 		} visitor(grid, idx_contour, resampled_point_parameters, 0.5 * compensation * M_PI, search_radius); | ||||
| 
 | ||||
| 		out.reserve(contour.size()); | ||||
| 		Point radius_vector(search_radius, search_radius); | ||||
| 		for (const Point &pt : contour) { | ||||
| 			visitor.init(contour, pt); | ||||
| 			grid.visit_cells_intersecting_box(BoundingBox(pt - radius_vector, pt + radius_vector), visitor); | ||||
| 			out.emplace_back(float(visitor.found ? std::min(visitor.distance, search_radius) : search_radius)); | ||||
| 
 | ||||
| #if 0 | ||||
| //#ifdef CONTOUR_DISTANCE_DEBUG_SVG
 | ||||
| 			if (out.back() < search_radius) { | ||||
| 				SVG svg(debug_out_path("contour_distance_filtered-%d-%d.svg", iRun, int(&pt - contour.data())).c_str(), bbox); | ||||
| 				svg.draw(expoly_grid); | ||||
| 				svg.draw_outline(Polygon(contour), "blue", scale_(0.01)); | ||||
| 				svg.draw(pt, "green", coord_t(scale_(0.1))); | ||||
| 				svg.draw(visitor.closest_point, "red", coord_t(scale_(0.1))); | ||||
| 				printf("contour_distance_filtered-%d-%d.svg - distance %lf\n", iRun, int(&pt - contour.data()), unscale<double>(out.back())); | ||||
| 			} | ||||
| #endif /* CONTOUR_DISTANCE_DEBUG_SVG */ | ||||
| 		} | ||||
| #ifdef CONTOUR_DISTANCE_DEBUG_SVG | ||||
| 		if (out.back() < search_radius) { | ||||
| 			SVG svg(debug_out_path("contour_distance_filtered-final-%d.svg", iRun).c_str(), bbox); | ||||
| 			svg.draw(expoly_grid); | ||||
| 			svg.draw_outline(Polygon(contour), "blue", scale_(0.01)); | ||||
| 			for (size_t i = 0; i < contour.size(); ++ i) | ||||
| 				svg.draw(contour[i], out[i] < float(search_radius - SCALED_EPSILON) ? "red" : "green", coord_t(scale_(0.1))); | ||||
| 		} | ||||
| #endif /* CONTOUR_DISTANCE_DEBUG_SVG */ | ||||
| 	} | ||||
| 
 | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| Points resample_polygon(const Points &contour, double dist, std::vector<ResampledPoint> &resampled_point_parameters) | ||||
| { | ||||
| 	Points out; | ||||
|  | @ -257,8 +445,8 @@ static inline void smooth_compensation(std::vector<float> &compensation, float s | |||
| 	std::vector<float> out(compensation); | ||||
| 	for (size_t iter = 0; iter < num_iterations; ++ iter) { | ||||
| 		for (size_t i = 0; i < compensation.size(); ++ i) { | ||||
| 			float prev = (i == 0) ? compensation.back() : compensation[i - 1]; | ||||
| 			float next = (i + 1 == compensation.size()) ? compensation.front() : compensation[i + 1]; | ||||
| 			float prev = prev_value_modulo(i, compensation); | ||||
| 			float next = next_value_modulo(i, compensation); | ||||
| 			float laplacian = compensation[i] * (1.f - strength) + 0.5f * strength * (prev + next); | ||||
| 			// Compensations are negative. Only apply the laplacian if it leads to lower compensation.
 | ||||
| 			out[i] = std::max(laplacian, compensation[i]); | ||||
|  | @ -267,30 +455,6 @@ static inline void smooth_compensation(std::vector<float> &compensation, float s | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| template<typename INDEX_TYPE, typename CONTAINER> | ||||
| static inline INDEX_TYPE prev_idx_cyclic(INDEX_TYPE idx, const CONTAINER &container) | ||||
| { | ||||
| 	if (idx == 0) | ||||
| 		idx = INDEX_TYPE(container.size()); | ||||
| 	return -- idx; | ||||
| } | ||||
| 
 | ||||
| template<typename INDEX_TYPE, typename CONTAINER> | ||||
| static inline INDEX_TYPE next_idx_cyclic(INDEX_TYPE idx, const CONTAINER &container) | ||||
| { | ||||
| 	if (++ idx == INDEX_TYPE(container.size())) | ||||
| 		idx = 0; | ||||
| 	return idx; | ||||
| } | ||||
| 
 | ||||
| template<class T, class U = T> | ||||
| static inline T exchange(T& obj, U&& new_value) | ||||
| { | ||||
|     T old_value = std::move(obj); | ||||
|     obj = std::forward<U>(new_value); | ||||
|     return old_value; | ||||
| } | ||||
| 
 | ||||
| static inline void smooth_compensation_banded(const Points &contour, float band, std::vector<float> &compensation, float strength, size_t num_iterations) | ||||
| { | ||||
| 	assert(contour.size() == compensation.size()); | ||||
|  | @ -302,13 +466,13 @@ static inline void smooth_compensation_banded(const Points &contour, float band, | |||
| 		for (int i = 0; i < int(compensation.size()); ++ i) { | ||||
| 			const Vec2f  pthis = contour[i].cast<float>(); | ||||
| 			 | ||||
| 			int		j     = prev_idx_cyclic(i, contour); | ||||
| 			int		j     = prev_idx_modulo(i, contour); | ||||
| 			Vec2f	pprev = contour[j].cast<float>(); | ||||
| 			float	prev  = compensation[j]; | ||||
| 			float	l2    = (pthis - pprev).squaredNorm(); | ||||
| 			if (l2 < dist_min2) { | ||||
| 				float l = sqrt(l2); | ||||
| 				int jprev = exchange(j, prev_idx_cyclic(j, contour)); | ||||
| 				int jprev = exchange(j, prev_idx_modulo(j, contour)); | ||||
| 				while (j != i) { | ||||
| 					const Vec2f pp = contour[j].cast<float>(); | ||||
| 					const float lthis = (pp - pprev).norm(); | ||||
|  | @ -323,17 +487,17 @@ static inline void smooth_compensation_banded(const Points &contour, float band, | |||
| 					prev  = use_min ? std::min(prev, compensation[j]) : compensation[j]; | ||||
| 					pprev = pp; | ||||
| 					l     = lnext; | ||||
| 					jprev = exchange(j, prev_idx_cyclic(j, contour)); | ||||
| 					jprev = exchange(j, prev_idx_modulo(j, contour)); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			j = next_idx_cyclic(i, contour); | ||||
| 			j = next_idx_modulo(i, contour); | ||||
| 			pprev = contour[j].cast<float>(); | ||||
| 			float next = compensation[j]; | ||||
| 			l2 = (pprev - pthis).squaredNorm(); | ||||
| 			if (l2 < dist_min2) { | ||||
| 				float l = sqrt(l2); | ||||
| 				int jprev = exchange(j, next_idx_cyclic(j, contour)); | ||||
| 				int jprev = exchange(j, next_idx_modulo(j, contour)); | ||||
| 				while (j != i) { | ||||
| 					const Vec2f pp = contour[j].cast<float>(); | ||||
| 					const float lthis = (pp - pprev).norm(); | ||||
|  | @ -348,7 +512,7 @@ static inline void smooth_compensation_banded(const Points &contour, float band, | |||
| 					next  = use_min ? std::min(next, compensation[j]) : compensation[j]; | ||||
| 					pprev = pp; | ||||
| 					l     = lnext; | ||||
| 					jprev = exchange(j, next_idx_cyclic(j, contour)); | ||||
| 					jprev = exchange(j, next_idx_modulo(j, contour)); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
|  | @ -361,7 +525,7 @@ static inline void smooth_compensation_banded(const Points &contour, float band, | |||
| } | ||||
| 
 | ||||
| ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, const Flow &external_perimeter_flow, const double compensation) | ||||
| { | ||||
| {	 | ||||
| 	// The contour shall be wide enough to apply the external perimeter plus compensation on both sides.
 | ||||
| 	double min_contour_width = double(external_perimeter_flow.scaled_width() + external_perimeter_flow.scaled_spacing()); | ||||
| 	double scaled_compensation = scale_(compensation); | ||||
|  | @ -369,39 +533,68 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, const Flow & | |||
| 	// Make the search radius a bit larger for the averaging in contour_distance over a fan of rays to work.
 | ||||
| 	double search_radius = min_contour_width_compensated + min_contour_width * 0.5; | ||||
| 
 | ||||
| 	EdgeGrid::Grid grid; | ||||
| 	ExPolygon simplified = input_expoly.simplify(SCALED_EPSILON).front(); | ||||
| 	BoundingBox bbox = get_extents(simplified.contour); | ||||
| 	bbox.offset(SCALED_EPSILON); | ||||
| 	grid.set_bbox(bbox); | ||||
| 	grid.create(simplified, coord_t(0.7 * search_radius)); | ||||
| 	std::vector<std::vector<float>> deltas; | ||||
| 	deltas.reserve(simplified.holes.size() + 1); | ||||
| 	ExPolygon resampled(simplified); | ||||
| 	double resample_interval = scale_(0.5); | ||||
| 	for (size_t idx_contour = 0; idx_contour <= simplified.holes.size(); ++ idx_contour) { | ||||
| 		Polygon &poly = (idx_contour == 0) ? resampled.contour : resampled.holes[idx_contour - 1]; | ||||
| 		std::vector<ResampledPoint> resampled_point_parameters; | ||||
| 		poly.points = resample_polygon(poly.points, resample_interval, resampled_point_parameters); | ||||
| 		std::vector<float> dists = contour_distance(grid, idx_contour, poly.points, resampled_point_parameters, search_radius); | ||||
| 		for (float &d : dists) { | ||||
| //			printf("Point %d, Distance: %lf\n", int(&d - dists.data()), unscale<double>(d));
 | ||||
| 			// Convert contour width to available compensation distance.
 | ||||
| 			if (d < min_contour_width) | ||||
| 				d = 0.f; | ||||
| 			else if (d > min_contour_width_compensated) | ||||
| 				d = - float(scaled_compensation); | ||||
| 			else | ||||
| 				d = - (d - float(min_contour_width)) / 2.f; | ||||
| 			assert(d >= - float(scaled_compensation) && d <= 0.f); | ||||
| 	BoundingBox bbox = get_extents(input_expoly.contour); | ||||
| 	Point 		bbox_size = bbox.size(); | ||||
| 	ExPolygon   out; | ||||
| 	if (bbox_size.x() < min_contour_width_compensated + SCALED_EPSILON || | ||||
| 		bbox_size.y() < min_contour_width_compensated + SCALED_EPSILON || | ||||
| 		input_expoly.area() < min_contour_width_compensated * min_contour_width_compensated * 5.) | ||||
| 	{ | ||||
| 		// The contour is tiny. Don't correct it.
 | ||||
| 		out = input_expoly; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		EdgeGrid::Grid grid; | ||||
| 		ExPolygon simplified = input_expoly.simplify(SCALED_EPSILON).front(); | ||||
| 		BoundingBox bbox = get_extents(simplified.contour); | ||||
| 		bbox.offset(SCALED_EPSILON); | ||||
| 		grid.set_bbox(bbox); | ||||
| 		grid.create(simplified, coord_t(0.7 * search_radius)); | ||||
| 		std::vector<std::vector<float>> deltas; | ||||
| 		deltas.reserve(simplified.holes.size() + 1); | ||||
| 		ExPolygon resampled(simplified); | ||||
| 		double resample_interval = scale_(0.5); | ||||
| 		for (size_t idx_contour = 0; idx_contour <= simplified.holes.size(); ++ idx_contour) { | ||||
| 			Polygon &poly = (idx_contour == 0) ? resampled.contour : resampled.holes[idx_contour - 1]; | ||||
| 			std::vector<ResampledPoint> resampled_point_parameters; | ||||
| 			poly.points = resample_polygon(poly.points, resample_interval, resampled_point_parameters); | ||||
| 			std::vector<float> dists = contour_distance2(grid, idx_contour, poly.points, resampled_point_parameters, scaled_compensation, search_radius); | ||||
| 			for (float &d : dists) { | ||||
| 	//			printf("Point %d, Distance: %lf\n", int(&d - dists.data()), unscale<double>(d));
 | ||||
| 				// Convert contour width to available compensation distance.
 | ||||
| 				if (d < min_contour_width) | ||||
| 					d = 0.f; | ||||
| 				else if (d > min_contour_width_compensated) | ||||
| 					d = - float(scaled_compensation); | ||||
| 				else | ||||
| 					d = - (d - float(min_contour_width)) / 2.f; | ||||
| 				assert(d >= - float(scaled_compensation) && d <= 0.f); | ||||
| 			} | ||||
| 	//		smooth_compensation(dists, 0.4f, 10);
 | ||||
| 			smooth_compensation_banded(poly.points, float(0.8 * resample_interval), dists, 0.3f, 3); | ||||
| 			deltas.emplace_back(dists); | ||||
| 		} | ||||
| 
 | ||||
| 		ExPolygons out_vec = variable_offset_inner_ex(resampled, deltas, 2.); | ||||
| 		if (out_vec.size() == 1) | ||||
| 			out = std::move(out_vec.front()); | ||||
| 		else { | ||||
| 			// Something went wrong, don't compensate.
 | ||||
| 			out = input_expoly; | ||||
| #ifdef TESTS_EXPORT_SVGS | ||||
| 			if (out_vec.size() > 1) { | ||||
| 				static int iRun = 0; | ||||
| 				SVG::export_expolygons(debug_out_path("elephant_foot_compensation-many_contours-%d.svg", iRun ++).c_str(), | ||||
| 					{ { { input_expoly },   { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, | ||||
| 					  { { out_vec },		{ "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } } }); | ||||
| 			} | ||||
| #endif /* TESTS_EXPORT_SVGS */ | ||||
| 			assert(out_vec.size() == 1); | ||||
| 		} | ||||
| //		smooth_compensation(dists, 0.4f, 10);
 | ||||
| 		smooth_compensation_banded(poly.points, float(0.8 * resample_interval), dists, 0.3f, 3); | ||||
| 		deltas.emplace_back(dists); | ||||
| 	} | ||||
| 
 | ||||
| 	ExPolygons out = variable_offset_inner_ex(resampled, deltas, 2.); | ||||
| 	return out.front(); | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| ExPolygons elephant_foot_compensation(const ExPolygons &input, const Flow &external_perimeter_flow, const double compensation) | ||||
|  |  | |||
|  | @ -77,6 +77,11 @@ public: | |||
|     void triangulate_pp(Points *triangles) const; | ||||
|     void triangulate_p2t(Polygons* polygons) const; | ||||
|     Lines lines() const; | ||||
| 
 | ||||
|     // Number of contours (outer contour with holes).
 | ||||
|     size_t   		num_contours() const { return this->holes.size() + 1; } | ||||
|     Polygon& 		contour_or_hole(size_t idx) 		{ return (idx == 0) ? this->contour : this->holes[idx - 1]; } | ||||
|     const Polygon& 	contour_or_hole(size_t idx) const 	{ return (idx == 0) ? this->contour : this->holes[idx - 1]; } | ||||
| }; | ||||
| 
 | ||||
| inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; } | ||||
|  |  | |||
|  | @ -267,6 +267,15 @@ public: | |||
| 
 | ||||
|     //static inline std::string role_to_string(ExtrusionLoopRole role);
 | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
| 	bool validate() const { | ||||
| 		assert(this->first_point() == this->paths.back().polyline.points.back()); | ||||
| 		for (size_t i = 1; i < paths.size(); ++ i) | ||||
| 			assert(this->paths[i - 1].polyline.points.back() == this->paths[i].polyline.points.front()); | ||||
| 		return true; | ||||
| 	} | ||||
| #endif /* NDEBUG */ | ||||
| 
 | ||||
| private: | ||||
|     ExtrusionLoopRole m_loop_role; | ||||
| }; | ||||
|  |  | |||
|  | @ -158,43 +158,18 @@ void Fill3DHoneycomb::_fill_surface_single( | |||
|         ((this->layer_id/thickness_layers) % 2) + 1); | ||||
|      | ||||
|     // move pattern in place
 | ||||
|     for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) | ||||
|         it->translate(bb.min(0), bb.min(1)); | ||||
| 	for (Polyline &pl : polylines) | ||||
| 		pl.translate(bb.min); | ||||
| 
 | ||||
|     // clip pattern to boundaries
 | ||||
|     polylines = intersection_pl(polylines, (Polygons)expolygon); | ||||
|     // clip pattern to boundaries, chain the clipped polylines
 | ||||
|     Polylines polylines_chained = chain_polylines(intersection_pl(polylines, to_polygons(expolygon))); | ||||
| 
 | ||||
|     // connect lines
 | ||||
|     if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
 | ||||
|         ExPolygon expolygon_off; | ||||
|         { | ||||
|             ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON); | ||||
|             if (! expolygons_off.empty()) { | ||||
|                 // When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
 | ||||
|                 assert(expolygons_off.size() == 1); | ||||
|                 std::swap(expolygon_off, expolygons_off.front()); | ||||
|             } | ||||
|         } | ||||
|         bool first = true; | ||||
|         for (Polyline &polyline : chain_polylines(std::move(polylines))) { | ||||
|             if (! first) { | ||||
|                 // Try to connect the lines.
 | ||||
|                 Points &pts_end = polylines_out.back().points; | ||||
|                 const Point &first_point = polyline.points.front(); | ||||
|                 const Point &last_point = pts_end.back(); | ||||
|                 // TODO: we should also check that both points are on a fill_boundary to avoid 
 | ||||
|                 // connecting paths on the boundaries of internal regions
 | ||||
|                 if ((last_point - first_point).cast<double>().norm() <= 1.5 * distance &&  | ||||
|                     expolygon_off.contains(Line(last_point, first_point))) { | ||||
|                     // Append the polyline.
 | ||||
|                     pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|             // The lines cannot be connected.
 | ||||
|             polylines_out.emplace_back(std::move(polyline)); | ||||
|             first = false; | ||||
|         } | ||||
|     // connect lines if needed
 | ||||
|     if (! polylines_chained.empty()) { | ||||
|         if (params.dont_connect) | ||||
|             append(polylines_out, std::move(polylines_chained)); | ||||
|         else | ||||
|             this->connect_infill(std::move(polylines_chained), expolygon, polylines_out, params); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,10 @@ | |||
| #include <stdio.h> | ||||
| 
 | ||||
| #include "../ClipperUtils.hpp" | ||||
| #include "../EdgeGrid.hpp" | ||||
| #include "../Surface.hpp" | ||||
| #include "../PrintConfig.hpp" | ||||
| #include "../libslic3r.h" | ||||
| 
 | ||||
| #include "FillBase.hpp" | ||||
| #include "FillConcentric.hpp" | ||||
|  | @ -148,4 +150,838 @@ std::pair<float, Point> Fill::_infill_direction(const Surface *surface) const | |||
|     return std::pair<float, Point>(out_angle, out_shift); | ||||
| } | ||||
| 
 | ||||
| #if 0 | ||||
| // From pull request "Gyroid improvements" #2730 by @supermerill
 | ||||
| 
 | ||||
| /// cut poly between poly.point[idx_1] & poly.point[idx_1+1]
 | ||||
| /// add p1+-width to one part and p2+-width to the other one.
 | ||||
| /// add the "new" polyline to polylines (to part cut from poly)
 | ||||
| /// p1 & p2 have to be between poly.point[idx_1] & poly.point[idx_1+1]
 | ||||
| /// if idx_1 is ==0 or == size-1, then we don't need to create a new polyline.
 | ||||
| static void cut_polyline(Polyline &poly, Polylines &polylines, size_t idx_1, Point p1, Point p2) { | ||||
|     //reorder points
 | ||||
|     if (p1.distance_to_square(poly.points[idx_1]) > p2.distance_to_square(poly.points[idx_1])) { | ||||
|         Point temp = p2; | ||||
|         p2 = p1; | ||||
|         p1 = temp; | ||||
|     } | ||||
|     if (idx_1 == poly.points.size() - 1) { | ||||
|         //shouldn't be possible.
 | ||||
|         poly.points.erase(poly.points.end() - 1); | ||||
|     } else { | ||||
|         // create new polyline
 | ||||
|         Polyline new_poly; | ||||
|         //put points in new_poly
 | ||||
|         new_poly.points.push_back(p2); | ||||
|         new_poly.points.insert(new_poly.points.end(), poly.points.begin() + idx_1 + 1, poly.points.end()); | ||||
|         //erase&put points in poly
 | ||||
|         poly.points.erase(poly.points.begin() + idx_1 + 1, poly.points.end()); | ||||
|         poly.points.push_back(p1); | ||||
|         //safe test
 | ||||
|         if (poly.length() == 0) | ||||
|             poly.points = new_poly.points; | ||||
|         else | ||||
|             polylines.emplace_back(new_poly); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// the poly is like a polygon but with first_point != last_point (already removed)
 | ||||
| static void cut_polygon(Polyline &poly, size_t idx_1, Point p1, Point p2) { | ||||
|     //reorder points
 | ||||
|     if (p1.distance_to_square(poly.points[idx_1]) > p2.distance_to_square(poly.points[idx_1])) { | ||||
|         Point temp = p2; | ||||
|         p2 = p1; | ||||
|         p1 = temp; | ||||
|     } | ||||
|     //check if we need to rotate before cutting
 | ||||
|     if (idx_1 != poly.size() - 1) { | ||||
|         //put points in new_poly 
 | ||||
|         poly.points.insert(poly.points.end(), poly.points.begin(), poly.points.begin() + idx_1 + 1); | ||||
|         poly.points.erase(poly.points.begin(), poly.points.begin() + idx_1 + 1); | ||||
|     } | ||||
|     //put points in poly
 | ||||
|     poly.points.push_back(p1); | ||||
|     poly.points.insert(poly.points.begin(), p2); | ||||
| } | ||||
| 
 | ||||
| /// check if the polyline from pts_to_check may be at 'width' distance of a point in polylines_blocker
 | ||||
| /// it use equally_spaced_points with width/2 precision, so don't worry with pts_to_check number of points.
 | ||||
| /// it use the given polylines_blocker points, be sure to put enough of them to be reliable.
 | ||||
| /// complexity : N(pts_to_check.equally_spaced_points(width / 2)) x N(polylines_blocker.points)
 | ||||
| static bool collision(const Points &pts_to_check, const Polylines &polylines_blocker, const coordf_t width) { | ||||
|     //check if it's not too close to a polyline
 | ||||
|     coordf_t min_dist_square = width * width * 0.9 - SCALED_EPSILON; | ||||
|     Polyline better_polylines(pts_to_check); | ||||
|     Points better_pts = better_polylines.equally_spaced_points(width / 2); | ||||
|     for (const Point &p : better_pts) { | ||||
|         for (const Polyline &poly2 : polylines_blocker) { | ||||
|             for (const Point &p2 : poly2.points) { | ||||
|                 if (p.distance_to_square(p2) < min_dist_square) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| /// Try to find a path inside polylines that allow to go from p1 to p2.
 | ||||
| /// width if the width of the extrusion
 | ||||
| /// polylines_blockers are the array of polylines to check if the path isn't blocked by something.
 | ||||
| /// complexity: N(polylines.points) + a collision check after that if we finded a path: N(2(p2-p1)/width) x N(polylines_blocker.points)
 | ||||
| static Points get_frontier(Polylines &polylines, const Point& p1, const Point& p2, const coord_t width, const Polylines &polylines_blockers, coord_t max_size = -1) { | ||||
|     for (size_t idx_poly = 0; idx_poly < polylines.size(); ++idx_poly) { | ||||
|         Polyline &poly = polylines[idx_poly]; | ||||
|         if (poly.size() <= 1) continue; | ||||
| 
 | ||||
|         //loop?
 | ||||
|         if (poly.first_point() == poly.last_point()) { | ||||
|             //polygon : try to find a line for p1 & p2.
 | ||||
|             size_t idx_11, idx_12, idx_21, idx_22; | ||||
|             idx_11 = poly.closest_point_index(p1); | ||||
|             idx_12 = idx_11; | ||||
|             if (Line(poly.points[idx_11], poly.points[(idx_11 + 1) % (poly.points.size() - 1)]).distance_to(p1) < SCALED_EPSILON) { | ||||
|                 idx_12 = (idx_11 + 1) % (poly.points.size() - 1); | ||||
|             } else if (Line(poly.points[(idx_11 > 0) ? (idx_11 - 1) : (poly.points.size() - 2)], poly.points[idx_11]).distance_to(p1) < SCALED_EPSILON) { | ||||
|                 idx_11 = (idx_11 > 0) ? (idx_11 - 1) : (poly.points.size() - 2); | ||||
|             } else { | ||||
|                 continue; | ||||
|             } | ||||
|             idx_21 = poly.closest_point_index(p2); | ||||
|             idx_22 = idx_21; | ||||
|             if (Line(poly.points[idx_21], poly.points[(idx_21 + 1) % (poly.points.size() - 1)]).distance_to(p2) < SCALED_EPSILON) { | ||||
|                 idx_22 = (idx_21 + 1) % (poly.points.size() - 1); | ||||
|             } else if (Line(poly.points[(idx_21 > 0) ? (idx_21 - 1) : (poly.points.size() - 2)], poly.points[idx_21]).distance_to(p2) < SCALED_EPSILON) { | ||||
|                 idx_21 = (idx_21 > 0) ? (idx_21 - 1) : (poly.points.size() - 2); | ||||
|             } else { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             //edge case: on the same line
 | ||||
|             if (idx_11 == idx_21 && idx_12 == idx_22) { | ||||
|                 if (collision(Points() = { p1, p2 }, polylines_blockers, width)) return Points(); | ||||
|                 //break loop
 | ||||
|                 poly.points.erase(poly.points.end() - 1); | ||||
|                 cut_polygon(poly, idx_11, p1, p2); | ||||
|                 return Points() = { Line(p1, p2).midpoint() }; | ||||
|             } | ||||
| 
 | ||||
|             //compute distance & array for the ++ path
 | ||||
|             Points ret_1_to_2; | ||||
|             double dist_1_to_2 = p1.distance_to(poly.points[idx_12]); | ||||
|             ret_1_to_2.push_back(poly.points[idx_12]); | ||||
|             size_t max = idx_12 <= idx_21 ? idx_21+1 : poly.points.size(); | ||||
|             for (size_t i = idx_12 + 1; i < max; i++) { | ||||
|                 dist_1_to_2 += poly.points[i - 1].distance_to(poly.points[i]); | ||||
|                 ret_1_to_2.push_back(poly.points[i]); | ||||
|             } | ||||
|             if (idx_12 > idx_21) { | ||||
|                 dist_1_to_2 += poly.points.back().distance_to(poly.points.front()); | ||||
|                 ret_1_to_2.push_back(poly.points[0]); | ||||
|                 for (size_t i = 1; i <= idx_21; i++) { | ||||
|                     dist_1_to_2 += poly.points[i - 1].distance_to(poly.points[i]); | ||||
|                     ret_1_to_2.push_back(poly.points[i]); | ||||
|                 } | ||||
|             } | ||||
|             dist_1_to_2 += p2.distance_to(poly.points[idx_21]); | ||||
| 
 | ||||
|             //compute distance & array for the -- path
 | ||||
|             Points ret_2_to_1; | ||||
|             double dist_2_to_1 = p1.distance_to(poly.points[idx_11]); | ||||
|             ret_2_to_1.push_back(poly.points[idx_11]); | ||||
|             size_t min = idx_22 <= idx_11 ? idx_22 : 0; | ||||
|             for (size_t i = idx_11; i > min; i--) { | ||||
|                 dist_2_to_1 += poly.points[i - 1].distance_to(poly.points[i]); | ||||
|                 ret_2_to_1.push_back(poly.points[i - 1]); | ||||
|             } | ||||
|             if (idx_22 > idx_11) { | ||||
|                 dist_2_to_1 += poly.points.back().distance_to(poly.points.front()); | ||||
|                 ret_2_to_1.push_back(poly.points[poly.points.size() - 1]); | ||||
|                 for (size_t i = poly.points.size() - 1; i > idx_22; i--) { | ||||
|                     dist_2_to_1 += poly.points[i - 1].distance_to(poly.points[i]); | ||||
|                     ret_2_to_1.push_back(poly.points[i - 1]); | ||||
|                 } | ||||
|             } | ||||
|             dist_2_to_1 += p2.distance_to(poly.points[idx_22]); | ||||
| 
 | ||||
|             if (max_size < dist_2_to_1 && max_size < dist_1_to_2) { | ||||
|                 return Points(); | ||||
|             } | ||||
| 
 | ||||
|             //choose between the two direction (keep the short one)
 | ||||
|             if (dist_1_to_2 < dist_2_to_1) { | ||||
|                 if (collision(ret_1_to_2, polylines_blockers, width)) return Points(); | ||||
|                 //break loop
 | ||||
|                 poly.points.erase(poly.points.end() - 1); | ||||
|                 //remove points
 | ||||
|                 if (idx_12 <= idx_21) { | ||||
|                     poly.points.erase(poly.points.begin() + idx_12, poly.points.begin() + idx_21 + 1); | ||||
|                     if (idx_12 != 0) { | ||||
|                         cut_polygon(poly, idx_11, p1, p2); | ||||
|                     } //else : already cut at the good place
 | ||||
|                 } else { | ||||
|                     poly.points.erase(poly.points.begin() + idx_12, poly.points.end()); | ||||
|                     poly.points.erase(poly.points.begin(), poly.points.begin() + idx_21); | ||||
|                     cut_polygon(poly, poly.points.size() - 1, p1, p2); | ||||
|                 } | ||||
|                 return ret_1_to_2; | ||||
|             } else { | ||||
|                 if (collision(ret_2_to_1, polylines_blockers, width)) return Points(); | ||||
|                 //break loop
 | ||||
|                 poly.points.erase(poly.points.end() - 1); | ||||
|                 //remove points
 | ||||
|                 if (idx_22 <= idx_11) { | ||||
|                     poly.points.erase(poly.points.begin() + idx_22, poly.points.begin() + idx_11 + 1); | ||||
|                     if (idx_22 != 0) { | ||||
|                         cut_polygon(poly, idx_21, p1, p2); | ||||
|                     } //else : already cut at the good place
 | ||||
|                 } else { | ||||
|                     poly.points.erase(poly.points.begin() + idx_22, poly.points.end()); | ||||
|                     poly.points.erase(poly.points.begin(), poly.points.begin() + idx_11); | ||||
|                     cut_polygon(poly, poly.points.size() - 1, p1, p2); | ||||
|                 } | ||||
|                 return ret_2_to_1; | ||||
|             } | ||||
|         } else { | ||||
|             //polyline : try to find a line for p1 & p2.
 | ||||
|             size_t idx_1, idx_2; | ||||
|             idx_1 = poly.closest_point_index(p1); | ||||
|             if (idx_1 < poly.points.size() - 1 && Line(poly.points[idx_1], poly.points[idx_1 + 1]).distance_to(p1) < SCALED_EPSILON) { | ||||
|             } else if (idx_1 > 0 && Line(poly.points[idx_1 - 1], poly.points[idx_1]).distance_to(p1) < SCALED_EPSILON) { | ||||
|                 idx_1 = idx_1 - 1; | ||||
|             } else { | ||||
|                 continue; | ||||
|             } | ||||
|             idx_2 = poly.closest_point_index(p2); | ||||
|             if (idx_2 < poly.points.size() - 1 && Line(poly.points[idx_2], poly.points[idx_2 + 1]).distance_to(p2) < SCALED_EPSILON) { | ||||
|             } else if (idx_2 > 0 && Line(poly.points[idx_2 - 1], poly.points[idx_2]).distance_to(p2) < SCALED_EPSILON) { | ||||
|                 idx_2 = idx_2 - 1; | ||||
|             } else { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             //edge case: on the same line
 | ||||
|             if (idx_1 == idx_2) { | ||||
|                 if (collision(Points() = { p1, p2 }, polylines_blockers, width)) return Points(); | ||||
|                 cut_polyline(poly, polylines, idx_1, p1, p2); | ||||
|                 return Points() = { Line(p1, p2).midpoint() }; | ||||
|             } | ||||
| 
 | ||||
|             //create ret array
 | ||||
|             size_t first_idx = idx_1; | ||||
|             size_t last_idx = idx_2 + 1; | ||||
|             if (idx_1 > idx_2) { | ||||
|                 first_idx = idx_2; | ||||
|                 last_idx = idx_1 + 1; | ||||
|             } | ||||
|             Points p_ret; | ||||
|             p_ret.insert(p_ret.end(), poly.points.begin() + first_idx + 1, poly.points.begin() + last_idx); | ||||
| 
 | ||||
|             coordf_t length = 0; | ||||
|             for (size_t i = 1; i < p_ret.size(); i++) length += p_ret[i - 1].distance_to(p_ret[i]); | ||||
| 
 | ||||
|             if (max_size < length) { | ||||
|                 return Points(); | ||||
|             } | ||||
| 
 | ||||
|             if (collision(p_ret, polylines_blockers, width)) return Points(); | ||||
|             //cut polyline
 | ||||
|             poly.points.erase(poly.points.begin() + first_idx + 1, poly.points.begin() + last_idx); | ||||
|             cut_polyline(poly, polylines, first_idx, p1, p2); | ||||
|             //order the returned array to be p1->p2
 | ||||
|             if (idx_1 > idx_2) { | ||||
|                 std::reverse(p_ret.begin(), p_ret.end()); | ||||
|             } | ||||
|             return p_ret; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     return Points(); | ||||
| } | ||||
| 
 | ||||
| /// Connect the infill_ordered polylines, in this order, from the back point to the next front point.
 | ||||
| /// It uses only the boundary polygons to do so, and can't pass two times at the same place.
 | ||||
| /// It avoid passing over the infill_ordered's polylines (preventing local over-extrusion).
 | ||||
| /// return the connected polylines in polylines_out. Can output polygons (stored as polylines with first_point = last_point).
 | ||||
| /// complexity: worst: N(infill_ordered.points) x N(boundary.points)
 | ||||
| ///             typical: N(infill_ordered) x ( N(boundary.points) + N(infill_ordered.points) )
 | ||||
| void Fill::connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms) { | ||||
| 
 | ||||
|     //TODO: fallback to the quick & dirty old algorithm when n(points) is too high.
 | ||||
|     Polylines polylines_frontier = to_polylines(((Polygons)boundary)); | ||||
| 
 | ||||
|     Polylines polylines_blocker; | ||||
|     coord_t clip_size = scale_(this->spacing) * 2; | ||||
|     for (const Polyline &polyline : infill_ordered) { | ||||
|         if (polyline.length() > 2.01 * clip_size) { | ||||
|             polylines_blocker.push_back(polyline); | ||||
|             polylines_blocker.back().clip_end(clip_size); | ||||
|             polylines_blocker.back().clip_start(clip_size); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     //length between two lines
 | ||||
|     coordf_t ideal_length = (1 / params.density) * this->spacing; | ||||
| 
 | ||||
|     Polylines polylines_connected_first; | ||||
|     bool first = true; | ||||
|     for (const Polyline &polyline : infill_ordered) { | ||||
|         if (!first) { | ||||
|             // Try to connect the lines.
 | ||||
|             Points &pts_end = polylines_connected_first.back().points; | ||||
|             const Point &last_point = pts_end.back(); | ||||
|             const Point &first_point = polyline.points.front(); | ||||
|             if (last_point.distance_to(first_point) < scale_(this->spacing) * 10) { | ||||
|                 Points pts_frontier = get_frontier(polylines_frontier, last_point, first_point, scale_(this->spacing), polylines_blocker, (coord_t)scale_(ideal_length) * 2); | ||||
|                 if (!pts_frontier.empty()) { | ||||
|                     // The lines can be connected.
 | ||||
|                     pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end()); | ||||
|                     pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // The lines cannot be connected.
 | ||||
|         polylines_connected_first.emplace_back(std::move(polyline)); | ||||
| 
 | ||||
|         first = false; | ||||
|     } | ||||
| 
 | ||||
|     Polylines polylines_connected; | ||||
|     first = true; | ||||
|     for (const Polyline &polyline : polylines_connected_first) { | ||||
|         if (!first) { | ||||
|             // Try to connect the lines.
 | ||||
|             Points &pts_end = polylines_connected.back().points; | ||||
|             const Point &last_point = pts_end.back(); | ||||
|             const Point &first_point = polyline.points.front(); | ||||
| 
 | ||||
|             Polylines before = polylines_frontier; | ||||
|             Points pts_frontier = get_frontier(polylines_frontier, last_point, first_point, scale_(this->spacing), polylines_blocker); | ||||
|             if (!pts_frontier.empty()) { | ||||
|                 // The lines can be connected.
 | ||||
|                 pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end()); | ||||
|                 pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
|         // The lines cannot be connected.
 | ||||
|         polylines_connected.emplace_back(std::move(polyline)); | ||||
| 
 | ||||
|         first = false; | ||||
|     } | ||||
| 
 | ||||
|     //try to link to nearest point if possible
 | ||||
|     for (size_t idx1 = 0; idx1 < polylines_connected.size(); idx1++) { | ||||
|         size_t min_idx = 0; | ||||
|         coordf_t min_length = 0; | ||||
|         bool switch_id1 = false; | ||||
|         bool switch_id2 = false; | ||||
|         for (size_t idx2 = idx1 + 1; idx2 < polylines_connected.size(); idx2++) { | ||||
|             double last_first = polylines_connected[idx1].last_point().distance_to_square(polylines_connected[idx2].first_point()); | ||||
|             double first_first = polylines_connected[idx1].first_point().distance_to_square(polylines_connected[idx2].first_point()); | ||||
|             double first_last = polylines_connected[idx1].first_point().distance_to_square(polylines_connected[idx2].last_point()); | ||||
|             double last_last = polylines_connected[idx1].last_point().distance_to_square(polylines_connected[idx2].last_point()); | ||||
|             double min = std::min(std::min(last_first, last_last), std::min(first_first, first_last)); | ||||
|             if (min < min_length || min_length == 0) { | ||||
|                 min_idx = idx2; | ||||
|                 switch_id1 = (std::min(last_first, last_last) > std::min(first_first, first_last)); | ||||
|                 switch_id2 = (std::min(last_first, first_first) > std::min(last_last, first_last)); | ||||
|                 min_length = min; | ||||
|             } | ||||
|         } | ||||
|         if (min_idx > idx1 && min_idx < polylines_connected.size()){ | ||||
|             Points pts_frontier = get_frontier(polylines_frontier,  | ||||
|                 switch_id1 ? polylines_connected[idx1].first_point() : polylines_connected[idx1].last_point(),  | ||||
|                 switch_id2 ? polylines_connected[min_idx].last_point() : polylines_connected[min_idx].first_point(), | ||||
|                 scale_(this->spacing), polylines_blocker); | ||||
|             if (!pts_frontier.empty()) { | ||||
|                 if (switch_id1) polylines_connected[idx1].reverse(); | ||||
|                 if (switch_id2) polylines_connected[min_idx].reverse(); | ||||
|                 Points &pts_end = polylines_connected[idx1].points; | ||||
|                 pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end()); | ||||
|                 pts_end.insert(pts_end.end(), polylines_connected[min_idx].points.begin(), polylines_connected[min_idx].points.end()); | ||||
|                 polylines_connected.erase(polylines_connected.begin() + min_idx); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     //try to create some loops if possible
 | ||||
|     for (Polyline &polyline : polylines_connected) { | ||||
|         Points pts_frontier = get_frontier(polylines_frontier, polyline.last_point(), polyline.first_point(), scale_(this->spacing), polylines_blocker); | ||||
|         if (!pts_frontier.empty()) { | ||||
|             polyline.points.insert(polyline.points.end(), pts_frontier.begin(), pts_frontier.end()); | ||||
|             polyline.points.insert(polyline.points.begin(), polyline.points.back()); | ||||
|         } | ||||
|         polylines_out.emplace_back(polyline); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| struct ContourPointData { | ||||
| 	ContourPointData(float param) : param(param) {} | ||||
| 	// Eucleidean position of the contour point along the contour.
 | ||||
| 	float param				= 0.f; | ||||
| 	// Was the segment starting with this contour point extruded?
 | ||||
| 	bool  segment_consumed	= false; | ||||
| 	// Was this point extruded over?
 | ||||
| 	bool  point_consumed	= false; | ||||
| }; | ||||
| 
 | ||||
| // Verify whether the contour from point idx_start to point idx_end could be taken (whether all segments along the contour were not yet extruded).
 | ||||
| static bool could_take(const std::vector<ContourPointData> &contour_data, size_t idx_start, size_t idx_end) | ||||
| { | ||||
| 	assert(idx_start != idx_end); | ||||
| 	for (size_t i = idx_start; i != idx_end; ) { | ||||
| 		if (contour_data[i].segment_consumed || contour_data[i].point_consumed) | ||||
| 			return false; | ||||
| 		if (++ i == contour_data.size()) | ||||
| 			i = 0; | ||||
| 	} | ||||
| 	return ! contour_data[idx_end].point_consumed; | ||||
| } | ||||
| 
 | ||||
| // Connect end of pl1 to the start of pl2 using the perimeter contour.
 | ||||
| // The idx_start and idx_end are ordered so that the connecting polyline points will be taken with increasing indices.
 | ||||
| static void take(Polyline &pl1, Polyline &&pl2, const Points &contour, std::vector<ContourPointData> &contour_data, size_t idx_start, size_t idx_end, bool reversed) | ||||
| { | ||||
| #ifndef NDEBUG | ||||
| 	size_t num_points_initial = pl1.points.size(); | ||||
| 	assert(idx_start != idx_end); | ||||
| #endif /* NDEBUG */ | ||||
| 
 | ||||
| 	{ | ||||
| 		// Reserve memory at pl1 for the connecting contour and pl2.
 | ||||
| 		int new_points = int(idx_end) - int(idx_start) - 1; | ||||
| 		if (new_points < 0) | ||||
| 			new_points += int(contour.size()); | ||||
| 		pl1.points.reserve(pl1.points.size() + size_t(new_points) + pl2.points.size()); | ||||
| 	} | ||||
| 
 | ||||
| 	contour_data[idx_start].point_consumed   = true; | ||||
| 	contour_data[idx_start].segment_consumed = true; | ||||
| 	contour_data[idx_end  ].point_consumed   = true; | ||||
| 
 | ||||
| 	if (reversed) { | ||||
| 		size_t i = (idx_end == 0) ? contour_data.size() - 1 : idx_end - 1; | ||||
| 		while (i != idx_start) { | ||||
| 			contour_data[i].point_consumed   = true; | ||||
| 			contour_data[i].segment_consumed = true; | ||||
| 			pl1.points.emplace_back(contour[i]); | ||||
| 			if (i == 0) | ||||
| 				i = contour_data.size(); | ||||
| 			-- i; | ||||
| 		} | ||||
| 	} else { | ||||
| 		size_t i = idx_start; | ||||
| 		if (++ i == contour_data.size()) | ||||
| 			i = 0; | ||||
| 		while (i != idx_end) { | ||||
| 			contour_data[i].point_consumed   = true; | ||||
| 			contour_data[i].segment_consumed = true; | ||||
| 			pl1.points.emplace_back(contour[i]); | ||||
| 			if (++ i == contour_data.size()) | ||||
| 				i = 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	append(pl1.points, std::move(pl2.points)); | ||||
| } | ||||
| 
 | ||||
| // Return an index of start of a segment and a point of the clipping point at distance from the end of polyline.
 | ||||
| struct SegmentPoint { | ||||
| 	// Segment index, defining a line <idx_segment, idx_segment + 1).
 | ||||
| 	size_t idx_segment = std::numeric_limits<size_t>::max(); | ||||
| 	// Parameter of point in <0, 1) along the line <idx_segment, idx_segment + 1)
 | ||||
| 	double t; | ||||
| 	Vec2d  point; | ||||
| 
 | ||||
| 	bool valid() const { return idx_segment != std::numeric_limits<size_t>::max(); } | ||||
| }; | ||||
| 
 | ||||
| static inline SegmentPoint clip_start_segment_and_point(const Points &polyline, double distance) | ||||
| { | ||||
| 	assert(polyline.size() >= 2); | ||||
| 	assert(distance > 0.); | ||||
| 	// Initialized to "invalid".
 | ||||
| 	SegmentPoint out; | ||||
| 	if (polyline.size() >= 2) { | ||||
| 		const double d2 = distance * distance; | ||||
| 	    Vec2d pt_prev = polyline.front().cast<double>(); | ||||
| 		for (int i = 1; i < polyline.size(); ++ i) { | ||||
| 			Vec2d pt = polyline[i].cast<double>(); | ||||
| 			Vec2d v = pt - pt_prev; | ||||
| 	        double l2 = v.squaredNorm(); | ||||
| 	        if (l2 > d2) { | ||||
| 	        	out.idx_segment = i; | ||||
| 	        	out.t 			= distance / sqrt(l2); | ||||
| 	        	out.point 		= pt + out.t * v; | ||||
| 	            break; | ||||
| 	        } | ||||
| 	        distance -= sqrt(l2); | ||||
| 	        pt_prev = pt; | ||||
| 	    } | ||||
| 	} | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| static inline SegmentPoint clip_end_segment_and_point(const Points &polyline, double distance) | ||||
| { | ||||
| 	assert(polyline.size() >= 2); | ||||
| 	assert(distance > 0.); | ||||
| 	// Initialized to "invalid".
 | ||||
| 	SegmentPoint out; | ||||
| 	if (polyline.size() >= 2) { | ||||
| 		const double d2 = distance * distance; | ||||
| 	    Vec2d pt_next = polyline.back().cast<double>(); | ||||
| 		for (int i = int(polyline.size()) - 2; i >= 0; -- i) { | ||||
| 			Vec2d pt = polyline[i].cast<double>(); | ||||
| 			Vec2d v = pt - pt_next; | ||||
| 	        double l2 = v.squaredNorm(); | ||||
| 	        if (l2 > d2) { | ||||
| 	        	out.idx_segment = i; | ||||
| 	        	out.t 			= distance / sqrt(l2); | ||||
| 	        	out.point 		= pt + out.t * v; | ||||
| 	            break; | ||||
| 	        } | ||||
| 	        distance -= sqrt(l2); | ||||
| 	        pt_next = pt; | ||||
| 	    } | ||||
| 	} | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| static inline double segment_point_distance_squared(const Vec2d &p1a, const Vec2d &p1b, const Vec2d &p2) | ||||
| { | ||||
|     const Vec2d   v  = p1b - p1a; | ||||
|     const Vec2d   va = p2  - p1a; | ||||
|     const double  l2 = v.squaredNorm(); | ||||
|     if (l2 < EPSILON) | ||||
|         // p1a == p1b
 | ||||
|         return va.squaredNorm(); | ||||
|     // Project p2 onto the (p1a, p1b) segment.
 | ||||
|     const double t = va.dot(v); | ||||
|     if (t < 0.) | ||||
|     	return va.squaredNorm(); | ||||
|     else if (t > l2) | ||||
|     	return (p2 - p1b).squaredNorm(); | ||||
|     return ((t / l2) * v - va).squaredNorm(); | ||||
| } | ||||
| 
 | ||||
| // Distance to the closest point of line.
 | ||||
| static inline double min_distance_of_segments(const Vec2d &p1a, const Vec2d &p1b, const Vec2d &p2a, const Vec2d &p2b) | ||||
| { | ||||
|     Vec2d   v1 		= p1b - p1a; | ||||
|     double  l1_2 	= v1.squaredNorm(); | ||||
|     if (l1_2 < EPSILON) | ||||
|         // p1a == p1b: Return distance of p1a from the (p2a, p2b) segment.
 | ||||
|         return segment_point_distance_squared(p2a, p2b, p1a); | ||||
| 
 | ||||
|     Vec2d   v2 		= p2b - p2a; | ||||
|     double  l2_2 	= v2.squaredNorm(); | ||||
|     if (l2_2 < EPSILON) | ||||
|         // p2a == p2b: Return distance of p2a from the (p1a, p1b) segment.
 | ||||
|         return segment_point_distance_squared(p1a, p1b, p2a); | ||||
| 
 | ||||
|     // Project p2a, p2b onto the (p1a, p1b) segment.
 | ||||
| 	auto project_p2a_p2b_onto_seg_p1a_p1b = [](const Vec2d& p1a, const Vec2d& p1b, const Vec2d& p2a, const Vec2d& p2b, const Vec2d& v1, const double l1_2) { | ||||
| 		Vec2d   v1a2a = p2a - p1a; | ||||
| 		Vec2d   v1a2b = p2b - p1a; | ||||
| 		double 	t1 = v1a2a.dot(v1); | ||||
| 		double 	t2 = v1a2b.dot(v1); | ||||
| 		if (t1 <= 0.) { | ||||
| 			if (t2 <= 0.) | ||||
| 				// Both p2a and p2b are left of v1.
 | ||||
| 				return (((t1 < t2) ? p2b : p2a) - p1a).squaredNorm(); | ||||
| 			else if (t2 < l1_2) | ||||
| 				// Project p2b onto the (p1a, p1b) segment.
 | ||||
| 				return ((t2 / l1_2) * v1 - v1a2b).squaredNorm(); | ||||
| 		} | ||||
| 		else if (t1 >= l1_2) { | ||||
| 			if (t2 >= l1_2) | ||||
| 				// Both p2a and p2b are right of v1.
 | ||||
| 				return (((t1 < t2) ? p2a : p2b) - p1b).squaredNorm(); | ||||
| 			else if (t2 < l1_2) | ||||
| 				// Project p2b onto the (p1a, p1b) segment.
 | ||||
| 				return ((t2 / l1_2) * v1 - v1a2b).squaredNorm(); | ||||
| 		} | ||||
| 		else { | ||||
| 			// Project p1b onto the (p1a, p1b) segment.
 | ||||
| 			double dist_min = ((t2 / l1_2) * v1 - v1a2a).squaredNorm(); | ||||
| 			if (t2 > 0. && t2 < l1_2) | ||||
| 				dist_min = std::min(dist_min, ((t2 / l1_2) * v1 - v1a2b).squaredNorm()); | ||||
| 			return dist_min; | ||||
| 		} | ||||
| 		return std::numeric_limits<double>::max(); | ||||
| 	}; | ||||
| 
 | ||||
| 	return std::min( | ||||
| 		project_p2a_p2b_onto_seg_p1a_p1b(p1a, p1b, p2a, p2b, v1, l1_2), | ||||
| 		project_p2a_p2b_onto_seg_p1a_p1b(p2a, p2b, p1a, p1b, v2, l2_2)); | ||||
| } | ||||
| 
 | ||||
| // Mark the segments of split boundary as consumed if they are very close to some of the infill line.
 | ||||
| void mark_boundary_segments_touching_infill( | ||||
| 	const std::vector<Points> 					&boundary, | ||||
| 	std::vector<std::vector<ContourPointData>> 	&boundary_data, | ||||
| 	const BoundingBox 							&boundary_bbox, | ||||
| 	const Polylines 							&infill, | ||||
| 	const double							     clip_distance, | ||||
| 	const double 								 distance_colliding) | ||||
| { | ||||
| 	EdgeGrid::Grid grid; | ||||
| 	grid.set_bbox(boundary_bbox); | ||||
| 	// Inflate the bounding box by a thick line width.
 | ||||
| 	grid.create(boundary, clip_distance + scale_(10.)); | ||||
| 
 | ||||
| 	struct Visitor { | ||||
| 		Visitor(const EdgeGrid::Grid &grid, const std::vector<Points> &boundary, std::vector<std::vector<ContourPointData>> &boundary_data, const double dist2_max) : | ||||
| 			grid(grid), boundary(boundary), boundary_data(boundary_data), dist2_max(dist2_max) {} | ||||
| 
 | ||||
| 		void init(const Vec2d &pt1, const Vec2d &pt2) { | ||||
| 			this->pt1 = &pt1; | ||||
| 			this->pt2 = &pt2; | ||||
| 		} | ||||
| 
 | ||||
| 		bool operator()(coord_t iy, coord_t ix) { | ||||
| 			// Called with a row and colum of the grid cell, which is intersected by a line.
 | ||||
| 			auto cell_data_range = this->grid.cell_data_range(iy, ix); | ||||
| 			for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) { | ||||
| 				// End points of the line segment and their vector.
 | ||||
| 				auto segment = this->grid.segment(*it_contour_and_segment); | ||||
| 				const Vec2d seg_pt1 = segment.first.cast<double>(); | ||||
| 				const Vec2d seg_pt2 = segment.second.cast<double>(); | ||||
| 				if (min_distance_of_segments(seg_pt1, seg_pt2, *this->pt1, *this->pt2) < this->dist2_max) { | ||||
| 					// Mark this boundary segment as touching the infill line.
 | ||||
| 					ContourPointData&bdp = boundary_data[it_contour_and_segment->first][it_contour_and_segment->second]; | ||||
| 					bdp.segment_consumed = true; | ||||
| 					// There is no need for checking seg_pt2 as it will be checked the next time.
 | ||||
| 					if (segment_point_distance_squared(*this->pt1, *this->pt2, seg_pt1) < this->dist2_max) | ||||
| 						bdp.point_consumed = true; | ||||
| 				} | ||||
| 			} | ||||
| 			// Continue traversing the grid along the edge.
 | ||||
| 			return true; | ||||
| 		} | ||||
| 
 | ||||
| 		const EdgeGrid::Grid 			   			&grid; | ||||
| 		const std::vector<Points> 					&boundary; | ||||
| 		std::vector<std::vector<ContourPointData>> 	&boundary_data; | ||||
| 		// Maximum distance between the boundary and the infill line allowed to consider the boundary not touching the infill line.
 | ||||
| 		const double								 dist2_max; | ||||
| 
 | ||||
| 		const Vec2d 								*pt1; | ||||
| 		const Vec2d 								*pt2; | ||||
| 	} visitor(grid, boundary, boundary_data, distance_colliding * distance_colliding); | ||||
| 
 | ||||
| 	for (const Polyline &polyline : infill) { | ||||
| 		// Clip the infill polyline by the Eucledian distance along the polyline.
 | ||||
| 		SegmentPoint start_point = clip_start_segment_and_point(polyline.points, clip_distance); | ||||
| 		SegmentPoint end_point   = clip_end_segment_and_point(polyline.points, clip_distance); | ||||
| 		if (start_point.valid() && end_point.valid() &&  | ||||
| 			(start_point.idx_segment < end_point.idx_segment || (start_point.idx_segment == end_point.idx_segment && start_point.t < end_point.t))) { | ||||
| 			// The clipped polyline is non-empty.
 | ||||
| 			for (size_t point_idx = start_point.idx_segment; point_idx <= end_point.idx_segment; ++ point_idx) { | ||||
| //FIXME extend the EdgeGrid to suport tracing a thick line.
 | ||||
| #if 0 | ||||
| 				Point pt1, pt2; | ||||
| 				Vec2d pt1d, pt2d; | ||||
| 				if (point_idx == start_point.idx_segment) { | ||||
| 					pt1d = start_point.point; | ||||
| 					pt1  = pt1d.cast<coord_t>(); | ||||
| 				} else { | ||||
| 					pt1  = polyline.points[point_idx]; | ||||
| 					pt1d = pt1.cast<double>(); | ||||
| 				} | ||||
| 				if (point_idx == start_point.idx_segment) { | ||||
| 					pt2d = end_point.point; | ||||
| 					pt2  = pt1d.cast<coord_t>(); | ||||
| 				} else { | ||||
| 					pt2  = polyline.points[point_idx]; | ||||
| 					pt2d = pt2.cast<double>(); | ||||
| 				} | ||||
| 				visitor.init(pt1d, pt2d); | ||||
| 				grid.visit_cells_intersecting_thick_line(pt1, pt2, distance_colliding, visitor); | ||||
| #else | ||||
| 				Vec2d pt1 = (point_idx == start_point.idx_segment) ? start_point.point : polyline.points[point_idx].cast<double>(); | ||||
| 				Vec2d pt2 = (point_idx == end_point  .idx_segment) ? end_point  .point : polyline.points[point_idx].cast<double>(); | ||||
| 				visitor.init(pt1, pt2); | ||||
| 				// Simulate tracing of a thick line. This only works reliably if distance_colliding <= grid cell size.
 | ||||
| 				Vec2d v = (pt2 - pt1).normalized() * distance_colliding; | ||||
| 				Vec2d vperp(-v.y(), v.x()); | ||||
| 				Vec2d a = pt1 - v - vperp; | ||||
| 				Vec2d b = pt1 + v - vperp; | ||||
| 				grid.visit_cells_intersecting_line(a.cast<coord_t>(), b.cast<coord_t>(), visitor); | ||||
| 				a = pt1 - v + vperp; | ||||
| 				b = pt1 + v + vperp; | ||||
| 				grid.visit_cells_intersecting_line(a.cast<coord_t>(), b.cast<coord_t>(), visitor); | ||||
| #endif | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Fill::connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary_src, Polylines &polylines_out, const FillParams ¶ms) | ||||
| { | ||||
| 	assert(! infill_ordered.empty()); | ||||
| 	assert(! boundary_src.contour.points.empty()); | ||||
| 
 | ||||
| 	BoundingBox bbox = get_extents(boundary_src.contour); | ||||
| 	bbox.offset(SCALED_EPSILON); | ||||
| 
 | ||||
| 	// 1) Add the end points of infill_ordered to boundary_src.
 | ||||
| 	std::vector<Points>					   		boundary; | ||||
| 	std::vector<std::vector<ContourPointData>> 	boundary_data; | ||||
| 	boundary.assign(boundary_src.holes.size() + 1, Points()); | ||||
| 	boundary_data.assign(boundary_src.holes.size() + 1, std::vector<ContourPointData>()); | ||||
| 	// Mapping the infill_ordered end point to a (contour, point) of boundary.
 | ||||
| 	std::vector<std::pair<size_t, size_t>> map_infill_end_point_to_boundary; | ||||
| 	map_infill_end_point_to_boundary.assign(infill_ordered.size() * 2, std::pair<size_t, size_t>(std::numeric_limits<size_t>::max(), std::numeric_limits<size_t>::max())); | ||||
| 	{ | ||||
| 		// Project the infill_ordered end points onto boundary_src.
 | ||||
| 		std::vector<std::pair<EdgeGrid::Grid::ClosestPointResult, size_t>> intersection_points; | ||||
| 		{ | ||||
| 			EdgeGrid::Grid grid; | ||||
| 			grid.set_bbox(bbox); | ||||
| 			grid.create(boundary_src, scale_(10.)); | ||||
| 			intersection_points.reserve(infill_ordered.size() * 2); | ||||
| 			for (const Polyline &pl : infill_ordered) | ||||
| 				for (const Point *pt : { &pl.points.front(), &pl.points.back() }) { | ||||
| 					EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, SCALED_EPSILON); | ||||
| 					if (cp.valid()) { | ||||
| 						// The infill end point shall lie on the contour.
 | ||||
| 						assert(cp.distance < 2.); | ||||
| 						intersection_points.emplace_back(cp, (&pl - infill_ordered.data()) * 2 + (pt == &pl.points.front() ? 0 : 1)); | ||||
| 					} | ||||
| 				} | ||||
| 			std::sort(intersection_points.begin(), intersection_points.end(), [](const std::pair<EdgeGrid::Grid::ClosestPointResult, size_t> &cp1, const std::pair<EdgeGrid::Grid::ClosestPointResult, size_t> &cp2) { | ||||
| 				return   cp1.first.contour_idx < cp2.first.contour_idx || | ||||
| 						(cp1.first.contour_idx == cp2.first.contour_idx && | ||||
| 							(cp1.first.start_point_idx < cp2.first.start_point_idx || | ||||
| 								(cp1.first.start_point_idx == cp2.first.start_point_idx && cp1.first.t < cp2.first.t))); | ||||
| 			}); | ||||
| 		} | ||||
| 		auto it = intersection_points.begin(); | ||||
| 		auto it_end = intersection_points.end(); | ||||
| 		for (size_t idx_contour = 0; idx_contour <= boundary_src.holes.size(); ++ idx_contour) { | ||||
| 			const Polygon &contour_src = (idx_contour == 0) ? boundary_src.contour : boundary_src.holes[idx_contour - 1]; | ||||
| 			Points		  &contour_dst = boundary[idx_contour]; | ||||
| 			for (size_t idx_point = 0; idx_point < contour_src.points.size(); ++ idx_point) { | ||||
| 				contour_dst.emplace_back(contour_src.points[idx_point]); | ||||
| 				for (; it != it_end && it->first.contour_idx == idx_contour && it->first.start_point_idx == idx_point; ++ it) { | ||||
| 					// Add these points to the destination contour.
 | ||||
| 					const Vec2d pt1 = contour_src[idx_point].cast<double>(); | ||||
| 					const Vec2d pt2 = (idx_point + 1 == contour_src.size() ? contour_src.points.front() : contour_src.points[idx_point + 1]).cast<double>(); | ||||
| 					const Vec2d pt  = lerp(pt1, pt2, it->first.t); | ||||
| 					map_infill_end_point_to_boundary[it->second] = std::make_pair(idx_contour, contour_dst.size()); | ||||
| 					contour_dst.emplace_back(pt.cast<coord_t>()); | ||||
| 				} | ||||
| 			} | ||||
| 			// Parametrize the curve.
 | ||||
| 			std::vector<ContourPointData> &contour_data = boundary_data[idx_contour]; | ||||
| 			contour_data.reserve(contour_dst.size()); | ||||
| 			contour_data.emplace_back(ContourPointData(0.f)); | ||||
| 			for (size_t i = 1; i < contour_dst.size(); ++ i) | ||||
| 				contour_data.emplace_back(contour_data.back().param + (contour_dst[i].cast<float>() - contour_dst[i - 1].cast<float>()).norm()); | ||||
| 			contour_data.front().param = contour_data.back().param + (contour_dst.back().cast<float>() - contour_dst.front().cast<float>()).norm(); | ||||
| 		} | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
| 		assert(boundary.size() == boundary_src.num_contours()); | ||||
| 		assert(std::all_of(map_infill_end_point_to_boundary.begin(), map_infill_end_point_to_boundary.end(), | ||||
| 			[&boundary](const std::pair<size_t, size_t> &contour_point) { | ||||
| 				return contour_point.first < boundary.size() && contour_point.second < boundary[contour_point.first].size(); | ||||
| 			})); | ||||
| #endif /* NDEBUG */ | ||||
| 	} | ||||
| 
 | ||||
| 	// Mark the points and segments of split boundary as consumed if they are very close to some of the infill line.
 | ||||
| 	{ | ||||
| 		//const double clip_distance		= scale_(this->spacing);
 | ||||
| 		const double clip_distance		= 3. * scale_(this->spacing); | ||||
| 		const double distance_colliding = scale_(this->spacing); | ||||
| 		mark_boundary_segments_touching_infill(boundary, boundary_data, bbox, infill_ordered, clip_distance, distance_colliding); | ||||
| 	} | ||||
| 
 | ||||
| 	// Connection from end of one infill line to the start of another infill line.
 | ||||
| 	//const float length_max = scale_(this->spacing);
 | ||||
| //	const float length_max = scale_((2. / params.density) * this->spacing);
 | ||||
| 	const float length_max = scale_((1000. / params.density) * this->spacing); | ||||
| 	std::vector<size_t> merged_with(infill_ordered.size()); | ||||
| 	for (size_t i = 0; i < merged_with.size(); ++ i) | ||||
| 		merged_with[i] = i; | ||||
| 	struct ConnectionCost { | ||||
| 		ConnectionCost(size_t idx_first, double cost, bool reversed) : idx_first(idx_first), cost(cost), reversed(reversed) {} | ||||
| 		size_t  idx_first; | ||||
| 		double  cost; | ||||
| 		bool 	reversed; | ||||
| 	}; | ||||
| 	std::vector<ConnectionCost> connections_sorted; | ||||
| 	connections_sorted.reserve(infill_ordered.size() * 2 - 2); | ||||
| 	for (size_t idx_chain = 1; idx_chain < infill_ordered.size(); ++ idx_chain) { | ||||
| 		const Polyline 						&pl1 			= infill_ordered[idx_chain - 1]; | ||||
| 		const Polyline 						&pl2 			= infill_ordered[idx_chain]; | ||||
| 		const std::pair<size_t, size_t>		*cp1			= &map_infill_end_point_to_boundary[(idx_chain - 1) * 2 + 1]; | ||||
| 		const std::pair<size_t, size_t>		*cp2			= &map_infill_end_point_to_boundary[idx_chain * 2]; | ||||
| 		const std::vector<ContourPointData>	&contour_data	= boundary_data[cp1->first]; | ||||
| 		if (cp1->first == cp2->first) { | ||||
| 			// End points on the same contour. Try to connect them.
 | ||||
| 			float param_lo  = (cp1->second == 0) ? 0.f : contour_data[cp1->second].param; | ||||
| 			float param_hi  = (cp2->second == 0) ? 0.f : contour_data[cp2->second].param; | ||||
| 			float param_end = contour_data.front().param; | ||||
| 			bool  reversed  = false; | ||||
| 			if (param_lo > param_hi) { | ||||
| 				std::swap(param_lo, param_hi); | ||||
| 				reversed = true; | ||||
| 			} | ||||
| 			assert(param_lo >= 0.f && param_lo <= param_end); | ||||
| 			assert(param_hi >= 0.f && param_hi <= param_end); | ||||
| 			double len = param_hi - param_lo; | ||||
| 			if (len < length_max) | ||||
| 				connections_sorted.emplace_back(idx_chain - 1, len, reversed); | ||||
| 			len = param_lo + param_end - param_hi; | ||||
| 			if (len < length_max) | ||||
| 				connections_sorted.emplace_back(idx_chain - 1, len, ! reversed); | ||||
| 		} | ||||
| 	} | ||||
| 	std::sort(connections_sorted.begin(), connections_sorted.end(), [](const ConnectionCost& l, const ConnectionCost& r) { return l.cost < r.cost; }); | ||||
| 
 | ||||
| 	size_t idx_chain_last = 0; | ||||
| 	for (ConnectionCost &connection_cost : connections_sorted) { | ||||
| 		const std::pair<size_t, size_t>	*cp1 = &map_infill_end_point_to_boundary[connection_cost.idx_first * 2 + 1]; | ||||
| 		const std::pair<size_t, size_t>	*cp2 = &map_infill_end_point_to_boundary[(connection_cost.idx_first + 1) * 2]; | ||||
| 		assert(cp1->first == cp2->first); | ||||
| 		std::vector<ContourPointData>	&contour_data = boundary_data[cp1->first]; | ||||
| 		if (connection_cost.reversed) | ||||
| 			std::swap(cp1, cp2); | ||||
| 		if (could_take(contour_data, cp1->second, cp2->second)) { | ||||
| 			// Indices of the polygons to be connected.
 | ||||
| 			size_t idx_first  = connection_cost.idx_first; | ||||
| 			size_t idx_second = idx_first + 1; | ||||
| 			for (size_t last = idx_first;;) { | ||||
| 				size_t lower = merged_with[last]; | ||||
| 				if (lower == last) { | ||||
| 					merged_with[idx_first] = lower; | ||||
| 					idx_first = lower; | ||||
| 					break; | ||||
| 				} | ||||
| 				last = lower; | ||||
| 			} | ||||
| 			// Connect the two polygons using the boundary contour.
 | ||||
| 			take(infill_ordered[idx_first], std::move(infill_ordered[idx_second]), boundary[cp1->first], contour_data, cp1->second, cp2->second, connection_cost.reversed); | ||||
| 			// Mark the second polygon as merged with the first one.
 | ||||
| 			merged_with[idx_second] = merged_with[idx_first]; | ||||
| 		} | ||||
| 	} | ||||
| 	polylines_out.reserve(polylines_out.size() + std::count_if(infill_ordered.begin(), infill_ordered.end(), [](const Polyline &pl) { return ! pl.empty(); })); | ||||
| 	for (Polyline &pl : infill_ordered) | ||||
| 		if (! pl.empty()) | ||||
| 			polylines_out.emplace_back(std::move(pl)); | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -111,6 +111,8 @@ protected: | |||
| 
 | ||||
|     virtual std::pair<float, Point> _infill_direction(const Surface *surface) const; | ||||
| 
 | ||||
|     void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms); | ||||
| 
 | ||||
| public: | ||||
|     static coord_t  _adjust_solid_spacing(const coord_t width, const coord_t distance); | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,19 +31,26 @@ static inline double f(double x, double z_sin, double z_cos, bool vertical, bool | |||
| 
 | ||||
| static inline Polyline make_wave( | ||||
|     const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor, | ||||
|     double z_cos, double z_sin, bool vertical) | ||||
|     double z_cos, double z_sin, bool vertical, bool flip) | ||||
| { | ||||
|     std::vector<Vec2d> points = one_period; | ||||
|     double period = points.back()(0); | ||||
|     points.pop_back(); | ||||
|     int n = points.size(); | ||||
|     do { | ||||
|         points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1))); | ||||
|     } while (points.back()(0) < width); | ||||
|     points.back()(0) = width; | ||||
|     if (width != period) // do not extend if already truncated
 | ||||
|     { | ||||
|         points.reserve(one_period.size() * floor(width / period)); | ||||
|         points.pop_back(); | ||||
| 
 | ||||
|         int n = points.size(); | ||||
|         do { | ||||
|             points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1))); | ||||
|         } while (points.back()(0) < width - EPSILON); | ||||
| 
 | ||||
|         points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip))); | ||||
|     } | ||||
| 
 | ||||
|     // and construct the final polyline to return:
 | ||||
|     Polyline polyline; | ||||
|     polyline.points.reserve(points.size()); | ||||
|     for (auto& point : points) { | ||||
|         point(1) += offset; | ||||
|         point(1) = clamp(0., height, double(point(1))); | ||||
|  | @ -55,45 +62,56 @@ static inline Polyline make_wave( | |||
|     return polyline; | ||||
| } | ||||
| 
 | ||||
| static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip) | ||||
| static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance) | ||||
| { | ||||
|     std::vector<Vec2d> points; | ||||
|     double dx = M_PI_4; // very coarse spacing to begin with
 | ||||
|     double dx = M_PI_2; // exact coordinates on main inflexion lobes
 | ||||
|     double limit = std::min(2*M_PI, width); | ||||
|     for (double x = 0.; x < limit + EPSILON; x += dx) {  // so the last point is there too
 | ||||
|         x = std::min(x, limit); | ||||
|         points.emplace_back(Vec2d(x,f(x, z_sin,z_cos, vertical, flip))); | ||||
|     } | ||||
|     points.reserve(ceil(limit / tolerance / 3)); | ||||
| 
 | ||||
|     // now we will check all internal points and in case some are too far from the line connecting its neighbours,
 | ||||
|     // we will add one more point on each side:
 | ||||
|     const double tolerance = .1; | ||||
|     for (unsigned int i=1;i<points.size()-1;++i) { | ||||
|         auto& lp = points[i-1]; // left point
 | ||||
|         auto& tp = points[i];   // this point
 | ||||
|         Vec2d lrv = tp - lp; | ||||
|         auto& rp = points[i+1]; // right point
 | ||||
|         // calculate distance of the point to the line:
 | ||||
|         double dist_mm = unscale<double>(scaleFactor) * std::abs(cross2(rp, lp) - cross2(rp - lp, tp)) / lrv.norm(); | ||||
|         if (dist_mm > tolerance) {                               // if the difference from straight line is more than this
 | ||||
|             double x = 0.5f * (points[i-1](0) + points[i](0)); | ||||
|             points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip))); | ||||
|             x = 0.5f * (points[i+1](0) + points[i](0)); | ||||
|             points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip))); | ||||
|             // we added the points to the end, but need them all in order
 | ||||
|             std::sort(points.begin(), points.end(), [](const Vec2d &lhs, const Vec2d &rhs){ return lhs < rhs; }); | ||||
|             // decrement i so we also check the first newly added point
 | ||||
|             --i; | ||||
|     for (double x = 0.; x < limit - EPSILON; x += dx) { | ||||
|         points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip))); | ||||
|     } | ||||
|     points.emplace_back(Vec2d(limit, f(limit, z_sin, z_cos, vertical, flip))); | ||||
| 
 | ||||
|     // piecewise increase in resolution up to requested tolerance
 | ||||
|     for(;;) | ||||
|     { | ||||
|         size_t size = points.size(); | ||||
|         for (unsigned int i = 1;i < size; ++i) { | ||||
|             auto& lp = points[i-1]; // left point
 | ||||
|             auto& rp = points[i];   // right point
 | ||||
|             double x = lp(0) + (rp(0) - lp(0)) / 2; | ||||
|             double y = f(x, z_sin, z_cos, vertical, flip); | ||||
|             Vec2d ip = {x, y}; | ||||
|             if (std::abs(cross2(Vec2d(ip - lp), Vec2d(ip - rp))) > sqr(tolerance)) { | ||||
|                 points.emplace_back(std::move(ip)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (size == points.size()) | ||||
|             break; | ||||
|         else | ||||
|         { | ||||
|             // insert new points in order
 | ||||
|             std::sort(points.begin(), points.end(), | ||||
|                       [](const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0); }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return points; | ||||
| } | ||||
| 
 | ||||
| static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height) | ||||
| { | ||||
|     const double scaleFactor = scale_(line_spacing) / density_adjusted; | ||||
|  //scale factor for 5% : 8 712 388
 | ||||
|  // 1z = 10^-6 mm ?
 | ||||
| 
 | ||||
|     // tolerance in scaled units. clamp the maximum tolerance as there's
 | ||||
|     // no processing-speed benefit to do so beyond a certain point
 | ||||
|     const double tolerance = std::min(line_spacing / 2, FillGyroid::PatternTolerance) / unscale<double>(scaleFactor); | ||||
| 
 | ||||
|     //scale factor for 5% : 8 712 388
 | ||||
|     // 1z = 10^-6 mm ?
 | ||||
|     const double z     = gridZ / scaleFactor; | ||||
|     const double z_sin = sin(z); | ||||
|     const double z_cos = cos(z); | ||||
|  | @ -109,20 +127,27 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double | |||
|         std::swap(width,height); | ||||
|     } | ||||
| 
 | ||||
|     std::vector<Vec2d> one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time
 | ||||
|     std::vector<Vec2d> one_period_odd = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance); // creates one period of the waves, so it doesn't have to be recalculated all the time
 | ||||
|     flip = !flip;                                                                   // even polylines are a bit shifted
 | ||||
|     std::vector<Vec2d> one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance); | ||||
|     Polylines result; | ||||
| 
 | ||||
|     for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI)           // creates odd polylines
 | ||||
|             result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical)); | ||||
| 
 | ||||
|     flip = !flip;                                                                   // even polylines are a bit shifted
 | ||||
|     one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // updates the one period sample
 | ||||
|     for (double y0 = lower_bound + M_PI; y0 < upper_bound+EPSILON; y0 += 2*M_PI)    // creates even polylines
 | ||||
|             result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical)); | ||||
|     for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) { | ||||
|         // creates odd polylines
 | ||||
|         result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip)); | ||||
|         // creates even polylines
 | ||||
|         y0 += M_PI; | ||||
|         if (y0 < upper_bound + EPSILON) { | ||||
|             result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| // FIXME: needed to fix build on Mac on buildserver
 | ||||
| constexpr double FillGyroid::PatternTolerance; | ||||
| 
 | ||||
| void FillGyroid::_fill_surface_single( | ||||
|     const FillParams                ¶ms,  | ||||
|     unsigned int                     thickness_layers, | ||||
|  | @ -130,63 +155,52 @@ void FillGyroid::_fill_surface_single( | |||
|     ExPolygon                       &expolygon,  | ||||
|     Polylines                       &polylines_out) | ||||
| { | ||||
|     // no rotation is supported for this infill pattern (yet)
 | ||||
|     float infill_angle = this->angle + (CorrectionAngle * 2*M_PI) / 360.; | ||||
|     if(abs(infill_angle) >= EPSILON) | ||||
|         expolygon.rotate(-infill_angle); | ||||
| 
 | ||||
|     BoundingBox bb = expolygon.contour.bounding_box(); | ||||
|     // Density adjusted to have a good %of weight.
 | ||||
|     double      density_adjusted = std::max(0., params.density * 2.44); | ||||
|     double      density_adjusted = std::max(0., params.density * DensityAdjust); | ||||
|     // Distance between the gyroid waves in scaled coordinates.
 | ||||
|     coord_t     distance = coord_t(scale_(this->spacing) / density_adjusted); | ||||
| 
 | ||||
|     // align bounding box to a multiple of our grid module
 | ||||
|     bb.merge(_align_to_grid(bb.min, Point(2.*M_PI*distance, 2.*M_PI*distance))); | ||||
|     bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance))); | ||||
| 
 | ||||
|     // generate pattern
 | ||||
|     Polylines   polylines = make_gyroid_waves( | ||||
|     Polylines polylines = make_gyroid_waves( | ||||
|         scale_(this->z), | ||||
|         density_adjusted, | ||||
|         this->spacing, | ||||
|         ceil(bb.size()(0) / distance) + 1., | ||||
|         ceil(bb.size()(1) / distance) + 1.); | ||||
|      | ||||
|     // move pattern in place
 | ||||
|     for (Polyline &polyline : polylines) | ||||
|         polyline.translate(bb.min(0), bb.min(1)); | ||||
| 
 | ||||
|     // clip pattern to boundaries
 | ||||
|     polylines = intersection_pl(polylines, (Polygons)expolygon); | ||||
| 	// shift the polyline to the grid origin
 | ||||
| 	for (Polyline &pl : polylines) | ||||
| 		pl.translate(bb.min); | ||||
| 
 | ||||
|     // connect lines
 | ||||
|     if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
 | ||||
|         ExPolygon expolygon_off; | ||||
|         { | ||||
|             ExPolygons expolygons_off = offset_ex(expolygon, (float)SCALED_EPSILON); | ||||
|             if (! expolygons_off.empty()) { | ||||
|                 // When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
 | ||||
|                 assert(expolygons_off.size() == 1); | ||||
|                 std::swap(expolygon_off, expolygons_off.front()); | ||||
|             } | ||||
|         } | ||||
|         bool first = true; | ||||
|         for (Polyline &polyline : chain_polylines(std::move(polylines))) { | ||||
|             if (! first) { | ||||
|                 // Try to connect the lines.
 | ||||
|                 Points &pts_end = polylines_out.back().points; | ||||
|                 const Point &first_point = polyline.points.front(); | ||||
|                 const Point &last_point = pts_end.back(); | ||||
|                 // TODO: we should also check that both points are on a fill_boundary to avoid 
 | ||||
|                 // connecting paths on the boundaries of internal regions
 | ||||
|                 // TODO: avoid crossing current infill path
 | ||||
|                 if ((last_point - first_point).cast<double>().norm() <= 5 * distance &&  | ||||
|                     expolygon_off.contains(Line(last_point, first_point))) { | ||||
|                     // Append the polyline.
 | ||||
|                     pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|             // The lines cannot be connected.
 | ||||
|             polylines_out.emplace_back(std::move(polyline)); | ||||
|             first = false; | ||||
|         } | ||||
| 	polylines = intersection_pl(polylines, to_polygons(expolygon)); | ||||
| 
 | ||||
|     if (! polylines.empty()) | ||||
| 		// remove too small bits (larger than longer)
 | ||||
| 		polylines.erase( | ||||
| 			std::remove_if(polylines.begin(), polylines.end(), [this](const Polyline &pl) { return pl.length() < scale_(this->spacing * 3); }), | ||||
| 			polylines.end()); | ||||
| 
 | ||||
| 	if (! polylines.empty()) { | ||||
| 		polylines = chain_polylines(polylines); | ||||
| 		// connect lines
 | ||||
| 		size_t polylines_out_first_idx = polylines_out.size(); | ||||
| 		if (params.dont_connect) | ||||
|         	append(polylines_out, std::move(polylines)); | ||||
|         else | ||||
|             this->connect_infill(std::move(polylines), expolygon, polylines_out, params); | ||||
| 	    // new paths must be rotated back
 | ||||
| 	    if (abs(infill_angle) >= EPSILON) { | ||||
| 	        for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++ it) | ||||
| 	        	it->rotate(infill_angle); | ||||
| 	    } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,6 +16,17 @@ public: | |||
|     // require bridge flow since most of this pattern hangs in air
 | ||||
|     virtual bool use_bridge_flow() const { return false; } | ||||
| 
 | ||||
|     // Correction applied to regular infill angle to maximize printing
 | ||||
|     // speed in default configuration (degrees)
 | ||||
|     static constexpr float CorrectionAngle = -45.; | ||||
| 
 | ||||
|     // Density adjustment to have a good %of weight.
 | ||||
|     static constexpr double DensityAdjust = 2.44; | ||||
| 
 | ||||
|     // Gyroid upper resolution tolerance (mm^-2)
 | ||||
|     static constexpr double PatternTolerance = 0.2; | ||||
| 
 | ||||
| 
 | ||||
| protected: | ||||
|     virtual void _fill_surface_single( | ||||
|         const FillParams                ¶ms,  | ||||
|  |  | |||
|  | @ -285,7 +285,7 @@ static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2 | |||
|     return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1))); | ||||
| } | ||||
| 
 | ||||
| std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const | ||||
| std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z) const | ||||
| { | ||||
|     if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) | ||||
|         throw std::invalid_argument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); | ||||
|  | @ -321,6 +321,15 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T | |||
|         gcode += gcodegen.unretract(); | ||||
|     } | ||||
| 
 | ||||
|     double current_z = gcodegen.writer().get_position().z(); | ||||
|     if (z == -1.) // in case no specific z was provided, print at current_z pos
 | ||||
|         z = current_z; | ||||
|     if (! is_approx(z, current_z)) { | ||||
|         gcode += gcodegen.writer().retract(); | ||||
|         gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); | ||||
|         gcode += gcodegen.writer().unretract(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Process the end filament gcode.
 | ||||
|     std::string end_filament_gcode_str; | ||||
|  | @ -387,16 +396,23 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T | |||
|     // A phony move to the end position at the wipe tower.
 | ||||
|     gcodegen.writer().travel_to_xy(end_pos.cast<double>()); | ||||
|     gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); | ||||
|     if (! is_approx(z, current_z)) { | ||||
|         gcode += gcodegen.writer().retract(); | ||||
|         gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer."); | ||||
|         gcode += gcodegen.writer().unretract(); | ||||
|     } | ||||
| 
 | ||||
|     // Prepare a future wipe.
 | ||||
|     gcodegen.m_wipe.path.points.clear(); | ||||
|     if (new_extruder_id >= 0) { | ||||
|         // Start the wipe at the current position.
 | ||||
|         gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos)); | ||||
|         // Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
 | ||||
|         gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,  | ||||
|             Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left, | ||||
|             end_pos.y()))); | ||||
|     else { | ||||
|         // Prepare a future wipe.
 | ||||
|         gcodegen.m_wipe.path.points.clear(); | ||||
|         if (new_extruder_id >= 0) { | ||||
|             // Start the wipe at the current position.
 | ||||
|             gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos)); | ||||
|             // Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
 | ||||
|             gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, | ||||
|                 Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left, | ||||
|                 end_pos.y()))); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Let the planner know we are traveling between objects.
 | ||||
|  | @ -522,7 +538,23 @@ std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, | |||
| 		if (m_layer_idx < (int)m_tool_changes.size()) { | ||||
| 			if (! (size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) | ||||
|                 throw std::runtime_error("Wipe tower generation failed, possibly due to empty first layer."); | ||||
| 			gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id); | ||||
| 
 | ||||
| 
 | ||||
|             // Calculate where the wipe tower layer will be printed. -1 means that print z will not change,
 | ||||
|             // resulting in a wipe tower with sparse layers.
 | ||||
|             double wipe_tower_z = -1; | ||||
|             bool ignore_sparse = false; | ||||
|             if (gcodegen.config().wipe_tower_no_sparse_layers.value) { | ||||
|                 wipe_tower_z = m_last_wipe_tower_print_z; | ||||
|                 ignore_sparse = (m_brim_done && m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool); | ||||
|                 if (m_tool_change_idx == 0 && ! ignore_sparse) | ||||
|                    wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; | ||||
|             } | ||||
| 
 | ||||
|             if (! ignore_sparse) { | ||||
|                 gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z); | ||||
|                 m_last_wipe_tower_print_z = wipe_tower_z; | ||||
|             } | ||||
| 		} | ||||
|         m_brim_done = true; | ||||
|     } | ||||
|  | @ -961,7 +993,7 @@ void GCode::_do_export(Print &print, FILE *file) | |||
|     // Write thumbnails using base64 encoding
 | ||||
|     if (thumbnail_data != nullptr) | ||||
|     { | ||||
|         const unsigned int max_row_length = 78; | ||||
|         const size_t max_row_length = 78; | ||||
| 
 | ||||
|         for (const ThumbnailData& data : *thumbnail_data) | ||||
|         { | ||||
|  | @ -972,19 +1004,21 @@ void GCode::_do_export(Print &print, FILE *file) | |||
|                 void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); | ||||
|                 if (png_data != nullptr) | ||||
|                 { | ||||
|                     _write_format(file, "\n;\n; thumbnail begin %dx%d\n", data.width, data.height); | ||||
|                     std::string encoded; | ||||
|                     encoded.resize(boost::beast::detail::base64::encoded_size(png_size)); | ||||
|                     encoded.resize(boost::beast::detail::base64::encode((void*)&encoded[0], (const void*)png_data, png_size)); | ||||
| 
 | ||||
|                     std::string encoded = boost::beast::detail::base64_encode((const std::uint8_t*)png_data, png_size); | ||||
|                     _write_format(file, "\n;\n; thumbnail begin %dx%d %d\n", data.width, data.height, encoded.size()); | ||||
| 
 | ||||
|                     unsigned int row_count = 0; | ||||
|                     while (encoded.length() > max_row_length) | ||||
|                     while (encoded.size() > max_row_length) | ||||
|                     { | ||||
|                         _write_format(file, "; %s\n", encoded.substr(0, max_row_length).c_str()); | ||||
|                         encoded = encoded.substr(max_row_length); | ||||
|                         ++row_count; | ||||
|                     } | ||||
| 
 | ||||
|                     if (encoded.length() > 0) | ||||
|                     if (encoded.size() > 0) | ||||
|                         _write_format(file, "; %s\n", encoded.c_str()); | ||||
| 
 | ||||
|                     _write(file, "; thumbnail end\n;\n"); | ||||
|  | @ -997,9 +1031,12 @@ void GCode::_do_export(Print &print, FILE *file) | |||
|                 size_t row_size = 4 * data.width; | ||||
|                 for (int r = (int)data.height - 1; r >= 0; --r) | ||||
|                 { | ||||
|                     std::string encoded = boost::beast::detail::base64_encode((const std::uint8_t*)(data.pixels.data() + r * row_size), row_size); | ||||
|                     std::string encoded; | ||||
|                     encoded.resize(boost::beast::detail::base64::encoded_size(row_size)); | ||||
|                     encoded.resize(boost::beast::detail::base64::encode((void*)&encoded[0], (const void*)(data.pixels.data() + r * row_size), row_size)); | ||||
| 
 | ||||
|                     unsigned int row_count = 0; | ||||
|                     while (encoded.length() > max_row_length) | ||||
|                     while (encoded.size() > max_row_length) | ||||
|                     { | ||||
|                         if (row_count == 0) | ||||
|                             _write_format(file, "; %s\n", encoded.substr(0, max_row_length).c_str()); | ||||
|  | @ -1010,7 +1047,7 @@ void GCode::_do_export(Print &print, FILE *file) | |||
|                         ++row_count; | ||||
|                     } | ||||
| 
 | ||||
|                     if (encoded.length() > 0) | ||||
|                     if (encoded.size() > 0) | ||||
|                     { | ||||
|                         if (row_count == 0) | ||||
|                             _write_format(file, "; %s\n", encoded.c_str()); | ||||
|  |  | |||
|  | @ -113,7 +113,7 @@ public: | |||
| 
 | ||||
| private: | ||||
|     WipeTowerIntegration& operator=(const WipeTowerIntegration&); | ||||
|     std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const; | ||||
|     std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const; | ||||
| 
 | ||||
|     // Postprocesses gcode: rotates and moves G1 extrusions and returns result
 | ||||
|     std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; | ||||
|  | @ -134,6 +134,7 @@ private: | |||
|     int                                                          m_tool_change_idx; | ||||
|     bool                                                         m_brim_done; | ||||
|     bool                                                         i_have_brim = false; | ||||
|     double                                                       m_last_wipe_tower_print_z = 0.f; | ||||
| }; | ||||
| 
 | ||||
| class GCode { | ||||
|  |  | |||
|  | @ -474,6 +474,7 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<fl | |||
|     m_z_pos(0.f), | ||||
|     m_is_first_layer(false), | ||||
|     m_bridging(float(config.wipe_tower_bridging)), | ||||
|     m_no_sparse_layers(config.wipe_tower_no_sparse_layers), | ||||
|     m_gcode_flavor(config.gcode_flavor), | ||||
|     m_current_tool(initial_tool), | ||||
|     wipe_volumes(wiping_matrix) | ||||
|  | @ -1145,9 +1146,10 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() | |||
|     writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
 | ||||
|                                  m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation); | ||||
| 
 | ||||
|     bool toolchanges_on_layer = m_layer_info->toolchanges_depth() > WT_EPSILON; | ||||
| 	box_coordinates box = fill_box; | ||||
|     for (int i=0;i<2;++i) { | ||||
|         if (m_layer_info->toolchanges_depth() < WT_EPSILON) { // there were no toolchanges on this layer
 | ||||
|         if (! toolchanges_on_layer) { | ||||
|             if (i==0) box.expand(m_perimeter_width); | ||||
|             else box.expand(-m_perimeter_width); | ||||
|         } | ||||
|  | @ -1201,9 +1203,12 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() | |||
| 
 | ||||
|     m_depth_traversed = m_wipe_tower_depth-m_perimeter_width; | ||||
| 
 | ||||
|     // Ask our writer about how much material was consumed:
 | ||||
|     if (m_current_tool < m_used_filament_length.size()) | ||||
|         m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); | ||||
| 
 | ||||
|     // Ask our writer about how much material was consumed.
 | ||||
|     // Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
 | ||||
|     if (! m_no_sparse_layers || toolchanges_on_layer) | ||||
|         if (m_current_tool < m_used_filament_length.size()) | ||||
|             m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); | ||||
| 
 | ||||
|     ToolChangeResult result; | ||||
|     result.priming      = false; | ||||
|  |  | |||
|  | @ -220,6 +220,7 @@ private: | |||
|     float           m_parking_pos_retraction    = 0.f; | ||||
|     float           m_extra_loading_move        = 0.f; | ||||
|     float           m_bridging                  = 0.f; | ||||
|     bool            m_no_sparse_layers          = false; | ||||
|     bool            m_set_extruder_trimpot      = false; | ||||
|     bool            m_adhesion                  = true; | ||||
|     GCodeFlavor     m_gcode_flavor; | ||||
|  |  | |||
|  | @ -46,9 +46,9 @@ public: | |||
| 		if (indices.empty()) | ||||
| 			clear(); | ||||
| 		else { | ||||
| 			// Allocate a next highest power of 2 nodes, because the incomplete binary tree will not have the leaves filled strictly from the left.
 | ||||
| 			// Allocate enough memory for a full binary tree.
 | ||||
| 			m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos); | ||||
| 			build_recursive(indices, 0, 0, 0, (int)(indices.size() - 1)); | ||||
| 			build_recursive(indices, 0, 0, 0, indices.size() - 1); | ||||
| 		} | ||||
| 		indices.clear(); | ||||
| 	} | ||||
|  | @ -81,7 +81,7 @@ public: | |||
| 
 | ||||
| private: | ||||
| 	// Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension.
 | ||||
| 	void build_recursive(std::vector<size_t> &input, size_t node, int dimension, int left, int right) | ||||
| 	void build_recursive(std::vector<size_t> &input, size_t node, const size_t dimension, const size_t left, const size_t right) | ||||
| 	{ | ||||
| 		if (left > right) | ||||
| 			return; | ||||
|  | @ -94,54 +94,56 @@ private: | |||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		// Partition the input sequence to two equal halves.
 | ||||
| 		int center = (left + right) >> 1; | ||||
| 		// Partition the input to left / right pieces of the same length to produce a balanced tree.
 | ||||
| 		size_t center = (left + right) / 2; | ||||
| 		partition_input(input, dimension, left, right, center); | ||||
| 		// Insert a node into the tree.
 | ||||
| 		m_nodes[node] = input[center]; | ||||
| 		// Partition the left and right subtrees.
 | ||||
| 		size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension; | ||||
| 		build_recursive(input, (node << 1) + 1, next_dimension, left,	    center - 1); | ||||
| 		build_recursive(input, (node << 1) + 2, next_dimension, center + 1, right); | ||||
| 		// Build up the left / right subtrees.
 | ||||
| 		size_t next_dimension = dimension; | ||||
| 		if (++ next_dimension == NumDimensions) | ||||
| 			next_dimension = 0; | ||||
| 		if (center > left) | ||||
| 			build_recursive(input, node * 2 + 1, next_dimension, left, center - 1); | ||||
| 		build_recursive(input, node * 2 + 2, next_dimension, center + 1, right); | ||||
| 	} | ||||
| 
 | ||||
| 	// Partition the input m_nodes <left, right> at k using QuickSelect method.
 | ||||
| 	// Partition the input m_nodes <left, right> at "k" and "dimension" using the QuickSelect method:
 | ||||
| 	// https://en.wikipedia.org/wiki/Quickselect
 | ||||
| 	void partition_input(std::vector<size_t> &input, int dimension, int left, int right, int k) const | ||||
| 	// Items left of the k'th item are lower than the k'th item in the "dimension", 
 | ||||
| 	// items right of the k'th item are higher than the k'th item in the "dimension", 
 | ||||
| 	void partition_input(std::vector<size_t> &input, const size_t dimension, size_t left, size_t right, const size_t k) const | ||||
| 	{ | ||||
| 		while (left < right) { | ||||
| 			// Guess the k'th element.
 | ||||
| 			// Pick the pivot as a median of first, center and last value.
 | ||||
| 			// Sort first, center and last values.
 | ||||
| 			int  center       = (left + right) >> 1; | ||||
| 			auto left_value   = this->coordinate(input[left],   dimension); | ||||
| 			auto center_value = this->coordinate(input[center], dimension); | ||||
| 			auto right_value  = this->coordinate(input[right],  dimension); | ||||
| 			if (center_value < left_value) { | ||||
| 				std::swap(input[left], input[center]); | ||||
| 				std::swap(left_value,  center_value); | ||||
| 			size_t center = (left + right) / 2; | ||||
| 			CoordType pivot; | ||||
| 			{ | ||||
| 				// Bubble sort the input[left], input[center], input[right], so that a median of the three values
 | ||||
| 				// will end up in input[center].
 | ||||
| 				CoordType left_value   = this->coordinate(input[left],   dimension); | ||||
| 				CoordType center_value = this->coordinate(input[center], dimension); | ||||
| 				CoordType right_value  = this->coordinate(input[right],  dimension); | ||||
| 				if (left_value > center_value) { | ||||
| 					std::swap(input[left], input[center]); | ||||
| 					std::swap(left_value,  center_value); | ||||
| 				} | ||||
| 				if (left_value > right_value) { | ||||
| 					std::swap(input[left], input[right]); | ||||
| 					right_value = left_value; | ||||
| 				} | ||||
| 				if (center_value > right_value) { | ||||
| 					std::swap(input[center], input[right]); | ||||
| 					center_value = right_value; | ||||
| 				} | ||||
| 				pivot = center_value; | ||||
| 			} | ||||
| 			if (right_value < left_value) { | ||||
| 				std::swap(input[left], input[right]); | ||||
| 				std::swap(left_value,  right_value); | ||||
| 			} | ||||
| 			if (right_value < center_value) { | ||||
| 				std::swap(input[center], input[right]); | ||||
| 				// No need to do that, result is not used.
 | ||||
| 				// std::swap(center_value,  right_value);
 | ||||
| 			} | ||||
| 			// Only two or three values are left and those are sorted already.
 | ||||
| 			if (left + 3 > right) | ||||
| 			if (right <= left + 2) | ||||
| 				// The <left, right> interval is already sorted.
 | ||||
| 				break; | ||||
| 			// left and right items are already at their correct positions.
 | ||||
| 			// input[left].point[dimension] <= input[center].point[dimension] <= input[right].point[dimension]
 | ||||
| 			// Move the pivot to the (right - 1) position.
 | ||||
| 			std::swap(input[center], input[right - 1]); | ||||
| 			// Pivot value.
 | ||||
| 			double pivot = this->coordinate(input[right - 1],  dimension); | ||||
| 			size_t i = left; | ||||
| 			size_t j = right - 1; | ||||
| 			std::swap(input[center], input[j]); | ||||
| 			// Partition the set based on the pivot.
 | ||||
| 			int i = left; | ||||
| 			int j = right - 1; | ||||
| 			for (;;) { | ||||
| 				// Skip left points that are already at correct positions.
 | ||||
| 				// Search will certainly stop at position (right - 1), which stores the pivot.
 | ||||
|  | @ -153,7 +155,7 @@ private: | |||
| 				std::swap(input[i], input[j]); | ||||
| 			} | ||||
| 			// Restore pivot to the center of the sequence.
 | ||||
| 			std::swap(input[i], input[right]); | ||||
| 			std::swap(input[i], input[right - 1]); | ||||
| 			// Which side the kth element is in?
 | ||||
| 			if (k < i) | ||||
| 				right = i - 1; | ||||
|  | @ -173,7 +175,7 @@ private: | |||
| 			return; | ||||
| 
 | ||||
| 		// Left / right child node index.
 | ||||
| 		size_t left  = (node << 1) + 1; | ||||
| 		size_t left  = node * 2 + 1; | ||||
| 		size_t right = left + 1; | ||||
| 		unsigned int mask = visitor(m_nodes[node], dimension); | ||||
| 		if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) { | ||||
|  |  | |||
|  | @ -201,6 +201,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option | |||
|             || opt_key == "wipe_tower" | ||||
|             || opt_key == "wipe_tower_width" | ||||
|             || opt_key == "wipe_tower_bridging" | ||||
|             || opt_key == "wipe_tower_no_sparse_layers" | ||||
|             || opt_key == "wiping_volumes_matrix" | ||||
|             || opt_key == "parking_pos_retraction" | ||||
|             || opt_key == "cooling_tube_retraction" | ||||
|  |  | |||
|  | @ -62,6 +62,11 @@ void PrintConfigDef::init_common_params() | |||
|     def->mode = comAdvanced; | ||||
|     def->set_default_value(new ConfigOptionString("")); | ||||
| 
 | ||||
|     def = this->add("thumbnails", coPoints); | ||||
|     def->label = L("Picture sizes to be stored into a .gcode and .sl1 files"); | ||||
|     def->mode = comExpert; | ||||
|     def->set_default_value(new ConfigOptionPoints()); | ||||
| 
 | ||||
|     def = this->add("layer_height", coFloat); | ||||
|     def->label = L("Layer height"); | ||||
|     def->category = L("Layers and Perimeters"); | ||||
|  | @ -1837,6 +1842,14 @@ void PrintConfigDef::init_fff_params() | |||
|     def->mode = comAdvanced; | ||||
|     def->set_default_value(new ConfigOptionBool(true)); | ||||
| 
 | ||||
|     def = this->add("wipe_tower_no_sparse_layers", coBool); | ||||
|     def->label = L("No sparse layers (EXPERIMENTAL)"); | ||||
|     def->tooltip = L("If enabled, the wipe tower will not be printed on layers with no toolchanges. " | ||||
|                      "On layers with a toolchange, extruder will travel downward to print the wipe tower. " | ||||
|                      "User is responsible for ensuring there is no collision with the print."); | ||||
|     def->mode = comAdvanced; | ||||
|     def->set_default_value(new ConfigOptionBool(false)); | ||||
| 
 | ||||
|     def = this->add("support_material", coBool); | ||||
|     def->label = L("Generate support material"); | ||||
|     def->category = L("Support material"); | ||||
|  | @ -2440,6 +2453,34 @@ void PrintConfigDef::init_sla_params() | |||
|     def->min = 0; | ||||
|     def->set_default_value(new ConfigOptionFloat(0.3)); | ||||
| 
 | ||||
|     def = this->add("bottle_volume", coFloat); | ||||
|     def->label = L("Bottle volume"); | ||||
|     def->tooltip = L("Bottle volume"); | ||||
|     def->sidetext = L("ml"); | ||||
|     def->min = 50; | ||||
|     def->set_default_value(new ConfigOptionFloat(1000.0)); | ||||
| 
 | ||||
|     def = this->add("bottle_weight", coFloat); | ||||
|     def->label = L("Bottle weight"); | ||||
|     def->tooltip = L("Bottle weight"); | ||||
|     def->sidetext = L("kg"); | ||||
|     def->min = 0; | ||||
|     def->set_default_value(new ConfigOptionFloat(1.0)); | ||||
| 
 | ||||
|     def = this->add("material_density", coFloat); | ||||
|     def->label = L("Density"); | ||||
|     def->tooltip = L("Density"); | ||||
|     def->sidetext = L("g/ml"); | ||||
|     def->min = 0; | ||||
|     def->set_default_value(new ConfigOptionFloat(1.0)); | ||||
| 
 | ||||
|     def = this->add("bottle_cost", coFloat); | ||||
|     def->label = L("Cost"); | ||||
|     def->tooltip = L("Cost"); | ||||
|     def->sidetext = L("money/bottle"); | ||||
|     def->min = 0; | ||||
|     def->set_default_value(new ConfigOptionFloat(0.0)); | ||||
| 
 | ||||
|     def = this->add("faded_layers", coInt); | ||||
|     def->label = L("Faded layers"); | ||||
|     def->tooltip = L("Number of the layers needed for the exposure time fade from initial exposure time to the exposure time"); | ||||
|  |  | |||
|  | @ -669,6 +669,7 @@ public: | |||
|     ConfigOptionStrings             start_filament_gcode; | ||||
|     ConfigOptionBool                single_extruder_multi_material; | ||||
|     ConfigOptionBool                single_extruder_multi_material_priming; | ||||
|     ConfigOptionBool                wipe_tower_no_sparse_layers; | ||||
|     ConfigOptionString              toolchange_gcode; | ||||
|     ConfigOptionFloat               travel_speed; | ||||
|     ConfigOptionBool                use_firmware_retraction; | ||||
|  | @ -739,6 +740,7 @@ protected: | |||
|         OPT_PTR(retract_speed); | ||||
|         OPT_PTR(single_extruder_multi_material); | ||||
|         OPT_PTR(single_extruder_multi_material_priming); | ||||
|         OPT_PTR(wipe_tower_no_sparse_layers); | ||||
|         OPT_PTR(start_gcode); | ||||
|         OPT_PTR(start_filament_gcode); | ||||
|         OPT_PTR(toolchange_gcode); | ||||
|  | @ -1126,6 +1128,10 @@ class SLAMaterialConfig : public StaticPrintConfig | |||
|     STATIC_PRINT_CONFIG_CACHE(SLAMaterialConfig) | ||||
| public: | ||||
|     ConfigOptionFloat                       initial_layer_height; | ||||
|     ConfigOptionFloat                       bottle_cost; | ||||
|     ConfigOptionFloat                       bottle_volume; | ||||
|     ConfigOptionFloat                       bottle_weight; | ||||
|     ConfigOptionFloat                       material_density; | ||||
|     ConfigOptionFloat                       exposure_time; | ||||
|     ConfigOptionFloat                       initial_exposure_time; | ||||
|     ConfigOptionFloats                      material_correction; | ||||
|  | @ -1133,6 +1139,10 @@ protected: | |||
|     void initialize(StaticCacheBase &cache, const char *base_ptr) | ||||
|     { | ||||
|         OPT_PTR(initial_layer_height); | ||||
|         OPT_PTR(bottle_cost); | ||||
|         OPT_PTR(bottle_volume); | ||||
|         OPT_PTR(bottle_weight); | ||||
|         OPT_PTR(material_density); | ||||
|         OPT_PTR(exposure_time); | ||||
|         OPT_PTR(initial_exposure_time); | ||||
|         OPT_PTR(material_correction); | ||||
|  |  | |||
|  | @ -1607,7 +1607,11 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt | |||
|         "output_filename_format", | ||||
|         "fast_tilt_time", | ||||
|         "slow_tilt_time", | ||||
|         "area_fill" | ||||
|         "area_fill", | ||||
|         "bottle_cost", | ||||
|         "bottle_volume", | ||||
|         "bottle_weight", | ||||
|         "material_density" | ||||
|     }; | ||||
| 
 | ||||
|     std::vector<SLAPrintStep> steps; | ||||
|  |  | |||
|  | @ -237,11 +237,19 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals | |||
| 
 | ||||
| 	    // Chain the end points: find (num_segments - 1) shortest links not forming bifurcations or loops.
 | ||||
| 		assert(num_segments >= 2); | ||||
| #ifndef NDEBUG | ||||
| 		double distance_taken_last = 0.; | ||||
| #endif /* NDEBUG */ | ||||
| 		for (int iter = int(num_segments) - 2;; -- iter) { | ||||
| 			assert(validate_graph_and_queue()); | ||||
| 	    	// Take the first end point, for which the link points to the currently closest valid neighbor.
 | ||||
| 	    	EndPoint &end_point1 = *queue.top(); | ||||
| 	    	assert(end_point1.edge_out != nullptr); | ||||
| #ifndef NDEBUG | ||||
| 			// Each edge added shall be longer than the previous one taken.
 | ||||
| 			assert(end_point1.distance_out > distance_taken_last - SCALED_EPSILON); | ||||
| 			distance_taken_last = end_point1.distance_out; | ||||
| #endif /* NDEBUG */ | ||||
| 			assert(end_point1.edge_out != nullptr); | ||||
| 	    	// No point on the queue may be connected yet.
 | ||||
| 	    	assert(end_point1.chain_id == 0); | ||||
| 	    	// Take the closest end point to the first end point,
 | ||||
|  | @ -313,6 +321,10 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals | |||
| 				assert(next_idx < end_points.size()); | ||||
| 				end_point1.edge_out = &end_points[next_idx]; | ||||
| 				end_point1.distance_out = (end_points[next_idx].pos - end_point1.pos).squaredNorm(); | ||||
| #ifndef NDEBUG | ||||
| 				// Each edge shall be longer than the last one removed from the queue.
 | ||||
| 				assert(end_point1.distance_out > distance_taken_last - SCALED_EPSILON); | ||||
| #endif /* NDEBUG */ | ||||
| 				// Update position of this end point in the queue based on the distance calculated at the line above.
 | ||||
| 				queue.update(end_point1.heap_idx); | ||||
| 		    	//FIXME Remove the other end point from the KD tree.
 | ||||
|  | @ -460,18 +472,206 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near) | |||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| // Flip the sequences of polylines to lower the total length of connecting lines.
 | ||||
| // #define DEBUG_SVG_OUTPUT
 | ||||
| static inline void improve_ordering_by_segment_flipping(Polylines &polylines, bool fixed_start) | ||||
| { | ||||
| #ifndef NDEBUG | ||||
| 	auto cost = [&polylines]() { | ||||
| 		double sum = 0.; | ||||
| 		for (size_t i = 1; i < polylines.size(); ++i) | ||||
| 			sum += (polylines[i].first_point() - polylines[i - 1].last_point()).cast<double>().norm(); | ||||
| 		return sum; | ||||
| 	}; | ||||
| 	double cost_initial = cost(); | ||||
| 
 | ||||
| 	static int iRun = 0; | ||||
| 	++ iRun; | ||||
| 	BoundingBox bbox = get_extents(polylines); | ||||
| #ifdef DEBUG_SVG_OUTPUT | ||||
| 	{ | ||||
| 		SVG svg(debug_out_path("improve_ordering_by_segment_flipping-initial-%d.svg", iRun).c_str(), bbox); | ||||
| 		svg.draw(polylines); | ||||
| 		for (size_t i = 1; i < polylines.size(); ++ i) | ||||
| 			svg.draw(Line(polylines[i - 1].last_point(), polylines[i].first_point()), "red"); | ||||
| 	} | ||||
| #endif /* DEBUG_SVG_OUTPUT */ | ||||
| #endif /* NDEBUG */ | ||||
| 
 | ||||
| 	struct Connection { | ||||
| 		Connection(size_t heap_idx = std::numeric_limits<size_t>::max(), bool flipped = false) : heap_idx(heap_idx), flipped(flipped) {} | ||||
| 		// Position of this object on MutablePriorityHeap.
 | ||||
| 		size_t 	heap_idx; | ||||
| 		// Is segment_idx flipped?
 | ||||
| 		bool   	flipped; | ||||
| 
 | ||||
| 		double 			squaredNorm(const Polylines &polylines, const std::vector<Connection> &connections) const  | ||||
| 			{ return ((this + 1)->start_point(polylines, connections) - this->end_point(polylines, connections)).squaredNorm(); } | ||||
| 		double 			norm(const Polylines &polylines, const std::vector<Connection> &connections) const  | ||||
| 			{ return sqrt(this->squaredNorm(polylines, connections)); } | ||||
| 		double 			squaredNorm(const Polylines &polylines, const std::vector<Connection> &connections, bool try_flip1, bool try_flip2) const  | ||||
| 			{ return ((this + 1)->start_point(polylines, connections, try_flip2) - this->end_point(polylines, connections, try_flip1)).squaredNorm(); } | ||||
| 		double 			norm(const Polylines &polylines, const std::vector<Connection> &connections, bool try_flip1, bool try_flip2) const | ||||
| 			{ return sqrt(this->squaredNorm(polylines, connections, try_flip1, try_flip2)); } | ||||
| 		Vec2d			start_point(const Polylines &polylines, const std::vector<Connection> &connections, bool flip = false) const  | ||||
| 			{ const Polyline &pl = polylines[this - connections.data()]; return ((this->flipped == flip) ? pl.points.front() : pl.points.back()).cast<double>(); } | ||||
| 		Vec2d			end_point(const Polylines &polylines, const std::vector<Connection> &connections, bool flip = false) const  | ||||
| 			{ const Polyline &pl = polylines[this - connections.data()]; return ((this->flipped == flip) ? pl.points.back() : pl.points.front()).cast<double>(); } | ||||
| 
 | ||||
| 		bool 			in_queue() const { return this->heap_idx != std::numeric_limits<size_t>::max(); } | ||||
| 		void 			flip() { this->flipped = ! this->flipped; } | ||||
| 	}; | ||||
| 	std::vector<Connection> connections(polylines.size()); | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
| 	auto cost_flipped = [fixed_start, &polylines, &connections]() { | ||||
| 		assert(! fixed_start || ! connections.front().flipped); | ||||
| 		double sum = 0.; | ||||
| 		for (size_t i = 1; i < polylines.size(); ++ i) | ||||
| 			sum += connections[i - 1].norm(polylines, connections); | ||||
| 		return sum; | ||||
| 	}; | ||||
| 	double cost_prev = cost_flipped(); | ||||
| 	assert(std::abs(cost_initial - cost_prev) < SCALED_EPSILON); | ||||
| 
 | ||||
| 	auto print_statistics = [&polylines, &connections]() { | ||||
| #if 0 | ||||
| 		for (size_t i = 1; i < polylines.size(); ++ i) | ||||
| 			printf("Connecting %d with %d: Current length %lf flip(%d, %d), left flipped: %lf, right flipped: %lf, both flipped: %lf, \n", | ||||
| 				int(i - 1), int(i), | ||||
| 				unscale<double>(connections[i - 1].norm(polylines, connections)), | ||||
| 				int(connections[i - 1].flipped), int(connections[i].flipped), | ||||
| 				unscale<double>(connections[i - 1].norm(polylines, connections, true, false)), | ||||
| 				unscale<double>(connections[i - 1].norm(polylines, connections, false, true)), | ||||
| 				unscale<double>(connections[i - 1].norm(polylines, connections, true, true))); | ||||
| #endif | ||||
| 	}; | ||||
| 	print_statistics(); | ||||
| #endif /* NDEBUG */ | ||||
| 
 | ||||
|     // Initialize a MutablePriorityHeap of connections between polylines.
 | ||||
|     auto queue = make_mutable_priority_queue<Connection*>( | ||||
| 		[](Connection *connection, size_t idx){ connection->heap_idx = idx; }, | ||||
| 		// Sort by decreasing connection distance.
 | ||||
|     	[&polylines, &connections](Connection *l, Connection *r){ return l->squaredNorm(polylines, connections) > r->squaredNorm(polylines, connections); }); | ||||
| 	queue.reserve(polylines.size() - 1); | ||||
|     for (size_t i = 0; i + 1 < polylines.size(); ++ i) | ||||
| 		queue.push(&connections[i]); | ||||
| 
 | ||||
| 	static constexpr size_t itercnt = 100; | ||||
| 	size_t iter = 0; | ||||
| 	for (; ! queue.empty() && iter < itercnt; ++ iter) { | ||||
| 		Connection &connection = *queue.top(); | ||||
| 		queue.pop(); | ||||
| 		connection.heap_idx = std::numeric_limits<size_t>::max(); | ||||
| 		size_t idx_first = &connection - connections.data(); | ||||
| 		// Try to flip segments starting with idx_first + 1 to the end.
 | ||||
| 		// Calculate the last segment to be flipped to improve the total path length.
 | ||||
| 		double length_current 			= connection.norm(polylines, connections); | ||||
| 		double length_flipped 			= connection.norm(polylines, connections, false, true); | ||||
| 		int    best_idx_forward			= int(idx_first); | ||||
| 		double best_improvement_forward = 0.; | ||||
| 		for (size_t i = idx_first + 1; i + 1 < connections.size(); ++ i) { | ||||
| 			length_current += connections[i].norm(polylines, connections); | ||||
| 			double this_improvement = length_current - length_flipped - connections[i].norm(polylines, connections, true, false); | ||||
| 			length_flipped += connections[i].norm(polylines, connections, true, true); | ||||
| 			if (this_improvement > best_improvement_forward) { | ||||
| 				best_improvement_forward = this_improvement; | ||||
| 				best_idx_forward = int(i); | ||||
| 			} | ||||
| //			if (length_flipped > 1.5 * length_current)
 | ||||
| //				break;
 | ||||
| 		} | ||||
| 		if (length_current - length_flipped > best_improvement_forward) | ||||
| 			// Best improvement by flipping up to the end.
 | ||||
| 			best_idx_forward = int(connections.size()) - 1; | ||||
| 		// Try to flip segments starting with idx_first - 1 to the start.
 | ||||
| 		// Calculate the last segment to be flipped to improve the total path length.
 | ||||
| 		length_current 					  = connection.norm(polylines, connections); | ||||
| 		length_flipped					  = connection.norm(polylines, connections, true, false); | ||||
| 		int    best_idx_backwards		  = int(idx_first); | ||||
| 		double best_improvement_backwards = 0.; | ||||
| 		for (int i = int(idx_first) - 1; i >= 0; -- i) { | ||||
| 			length_current += connections[i].norm(polylines, connections); | ||||
| 			double this_improvement = length_current - length_flipped - connections[i].norm(polylines, connections, false, true); | ||||
| 			length_flipped += connections[i].norm(polylines, connections, true, true); | ||||
| 			if (this_improvement > best_improvement_backwards) { | ||||
| 				best_improvement_backwards = this_improvement; | ||||
| 				best_idx_backwards = int(i); | ||||
| 			} | ||||
| //			if (length_flipped > 1.5 * length_current)
 | ||||
| //				break;
 | ||||
| 		} | ||||
| 		if (! fixed_start && length_current - length_flipped > best_improvement_backwards) | ||||
| 			// Best improvement by flipping up to the start including the first polyline.
 | ||||
| 			best_idx_backwards = -1; | ||||
| 		int update_begin = int(idx_first); | ||||
| 		int update_end   = best_idx_forward; | ||||
| 		if (best_improvement_backwards > 0. && best_improvement_backwards > best_improvement_forward) { | ||||
| 			// Flip the sequence of polylines from idx_first to best_improvement_forward + 1.
 | ||||
| 			update_begin = best_idx_backwards; | ||||
| 			update_end   = int(idx_first); | ||||
| 		} | ||||
| 		assert(update_begin <= update_end); | ||||
| 		if (update_begin == update_end) | ||||
| 			continue; | ||||
| 		for (int i = update_begin + 1; i <= update_end; ++ i) | ||||
| 			connections[i].flip(); | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
| 		double cost = cost_flipped(); | ||||
| 		assert(cost < cost_prev); | ||||
| 		cost_prev = cost; | ||||
| 		print_statistics(); | ||||
| #endif /* NDEBUG */ | ||||
| 
 | ||||
| 		update_end = std::min(update_end + 1, int(connections.size()) - 1); | ||||
| 		for (int i = std::max(0, update_begin); i < update_end; ++ i) { | ||||
| 			Connection &c = connections[i]; | ||||
| 			if (c.in_queue()) | ||||
| 				queue.update(c.heap_idx); | ||||
| 			else | ||||
| 				queue.push(&c); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Flip the segments based on the flip flag.
 | ||||
| 	for (Polyline &pl : polylines) | ||||
| 		if (connections[&pl - polylines.data()].flipped) | ||||
| 			pl.reverse(); | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
| 	double cost_final = cost(); | ||||
| #ifdef DEBUG_SVG_OUTPUT | ||||
| 	{ | ||||
| 		SVG svg(debug_out_path("improve_ordering_by_segment_flipping-final-%d.svg", iRun).c_str(), bbox); | ||||
| 		svg.draw(polylines); | ||||
| 		for (size_t i = 1; i < polylines.size(); ++ i) | ||||
| 		svg.draw(Line(polylines[i - 1].last_point(), polylines[i].first_point()), "red"); | ||||
| 	} | ||||
| #endif /* DEBUG_SVG_OUTPUT */ | ||||
| #endif /* NDEBUG */ | ||||
| 
 | ||||
| 	assert(cost_final <= cost_prev); | ||||
| 	assert(cost_final <= cost_initial); | ||||
| } | ||||
| 
 | ||||
| Polylines chain_polylines(Polylines &&polylines, const Point *start_near) | ||||
| { | ||||
| 	auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); }; | ||||
| 	std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), start_near); | ||||
| 	Polylines out; | ||||
| 	out.reserve(polylines.size());  | ||||
| 	for (auto &segment_and_reversal : ordered) { | ||||
| 		out.emplace_back(std::move(polylines[segment_and_reversal.first])); | ||||
| 		if (segment_and_reversal.second) | ||||
| 			out.back().reverse(); | ||||
| 	if (! polylines.empty()) { | ||||
| 		auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); }; | ||||
| 		std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), start_near); | ||||
| 		out.reserve(polylines.size());  | ||||
| 		for (auto &segment_and_reversal : ordered) { | ||||
| 			out.emplace_back(std::move(polylines[segment_and_reversal.first])); | ||||
| 			if (segment_and_reversal.second) | ||||
| 				out.back().reverse(); | ||||
| 		} | ||||
| 		if (out.size() > 1) | ||||
| 			improve_ordering_by_segment_flipping(out, start_near != nullptr); | ||||
| 	} | ||||
| 	return out;	 | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| template<class T> static inline T chain_path_items(const Points &points, const T &items) | ||||
|  |  | |||
|  | @ -165,6 +165,65 @@ template<class T> size_t next_highest_power_of_2(T v, | |||
|     return next_highest_power_of_2(uint32_t(v)); | ||||
| } | ||||
| 
 | ||||
| template<typename INDEX_TYPE> | ||||
| inline INDEX_TYPE prev_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count) | ||||
| { | ||||
| 	if (idx == 0) | ||||
| 		idx = count; | ||||
| 	return -- idx; | ||||
| } | ||||
| 
 | ||||
| template<typename INDEX_TYPE> | ||||
| inline INDEX_TYPE next_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count) | ||||
| { | ||||
| 	if (++ idx == count) | ||||
| 		idx = 0; | ||||
| 	return idx; | ||||
| } | ||||
| 
 | ||||
| template<typename CONTAINER_TYPE> | ||||
| inline typename CONTAINER_TYPE::size_type prev_idx_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)  | ||||
| {  | ||||
| 	return prev_idx_modulo(idx, container.size()); | ||||
| } | ||||
| 
 | ||||
| template<typename CONTAINER_TYPE> | ||||
| inline typename CONTAINER_TYPE::size_type next_idx_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) | ||||
| {  | ||||
| 	return next_idx_modulo(idx, container.size()); | ||||
| } | ||||
| 
 | ||||
| template<typename CONTAINER_TYPE> | ||||
| inline const typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) | ||||
| {  | ||||
| 	return container[prev_idx_modulo(idx, container.size())]; | ||||
| } | ||||
| 
 | ||||
| template<typename CONTAINER_TYPE> | ||||
| inline typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, CONTAINER_TYPE &container)  | ||||
| {  | ||||
| 	return container[prev_idx_modulo(idx, container.size())]; | ||||
| } | ||||
| 
 | ||||
| template<typename CONTAINER_TYPE> | ||||
| inline const typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) | ||||
| {  | ||||
| 	return container[next_idx_modulo(idx, container.size())]; | ||||
| } | ||||
| 
 | ||||
| template<typename CONTAINER_TYPE> | ||||
| inline typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, CONTAINER_TYPE &container) | ||||
| {  | ||||
| 	return container[next_idx_modulo(idx, container.size())]; | ||||
| } | ||||
| 
 | ||||
| template<class T, class U = T> | ||||
| inline T exchange(T& obj, U&& new_value) | ||||
| { | ||||
|     T old_value = std::move(obj); | ||||
|     obj = std::forward<U>(new_value); | ||||
|     return old_value; | ||||
| } | ||||
| 
 | ||||
| extern std::string xml_escape(std::string text); | ||||
| 
 | ||||
|  |  | |||
|  | @ -116,5 +116,7 @@ | |||
|     <string>NSApplication</string> | ||||
|     <key>NSHighResolutionCapable</key> | ||||
|     <true/> | ||||
|     <key>NSRequiresAquaSystemAppearance</key> | ||||
|     <true/> | ||||
|   </dict> | ||||
| </plist> | ||||
|  |  | |||
|  | @ -158,6 +158,7 @@ set(SLIC3R_GUI_SOURCES | |||
|     Utils/UndoRedo.hpp | ||||
|     Utils/HexFile.cpp | ||||
|     Utils/HexFile.hpp | ||||
|     Utils/Thread.hpp | ||||
| ) | ||||
| 
 | ||||
| if (APPLE) | ||||
|  |  | |||
|  | @ -204,6 +204,7 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c | |||
|     std::string cst_texture(custom_texture); | ||||
|     if (!cst_texture.empty()) | ||||
|     { | ||||
|         std::replace(cst_texture.begin(), cst_texture.end(), '\\', '/'); | ||||
|         if ((!boost::algorithm::iends_with(custom_texture, ".png") && !boost::algorithm::iends_with(custom_texture, ".svg")) || !boost::filesystem::exists(custom_texture)) | ||||
|             cst_texture = ""; | ||||
|     } | ||||
|  | @ -212,6 +213,7 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c | |||
|     std::string cst_model(custom_model); | ||||
|     if (!cst_model.empty()) | ||||
|     { | ||||
|         std::replace(cst_model.begin(), cst_model.end(), '\\', '/'); | ||||
|         if (!boost::algorithm::iends_with(custom_model, ".stl") || !boost::filesystem::exists(custom_model)) | ||||
|             cst_model = ""; | ||||
|     } | ||||
|  | @ -270,27 +272,13 @@ void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor) const | |||
| 
 | ||||
|     switch (m_type) | ||||
|     { | ||||
|     case MK2: | ||||
|     { | ||||
|         render_prusa(canvas, "mk2", theta > 90.0f); | ||||
|         break; | ||||
|     } | ||||
|     case MK3: | ||||
|     { | ||||
|         render_prusa(canvas, "mk3", theta > 90.0f); | ||||
|         break; | ||||
|     } | ||||
|     case SL1: | ||||
|     { | ||||
|         render_prusa(canvas, "sl1", theta > 90.0f); | ||||
|         break; | ||||
|     } | ||||
|     case MK2: { render_prusa(canvas, "mk2", theta > 90.0f); break; } | ||||
|     case MK3: { render_prusa(canvas, "mk3", theta > 90.0f); break; } | ||||
|     case SL1: { render_prusa(canvas, "sl1", theta > 90.0f); break; } | ||||
|     case MINI: { render_prusa(canvas, "mini", theta > 90.0f); break; } | ||||
|     case ENDER3: { render_prusa(canvas, "ender3", theta > 90.0f); break; } | ||||
|     default: | ||||
|     case Custom: | ||||
|     { | ||||
|         render_custom(canvas, theta > 90.0f); | ||||
|         break; | ||||
|     } | ||||
|     case Custom: { render_custom(canvas, theta > 90.0f); break; } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -362,22 +350,38 @@ Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const | |||
|         { | ||||
|             if (curr->config.has("bed_shape")) | ||||
|             { | ||||
|                 if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research") && (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values)) | ||||
|                 if (curr->vendor != nullptr) | ||||
|                 { | ||||
|                     if (boost::contains(curr->name, "SL1")) | ||||
|                     if ((curr->vendor->name == "Prusa Research") && (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values)) | ||||
|                     { | ||||
|                         type = SL1; | ||||
|                         break; | ||||
|                         if (boost::contains(curr->name, "SL1")) | ||||
|                         { | ||||
|                             type = SL1; | ||||
|                             break; | ||||
|                         } | ||||
|                         else if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5")) | ||||
|                         { | ||||
|                             type = MK3; | ||||
|                             break; | ||||
|                         } | ||||
|                         else if (boost::contains(curr->name, "MK2")) | ||||
|                         { | ||||
|                             type = MK2; | ||||
|                             break; | ||||
|                         } | ||||
|                         else if (boost::contains(curr->name, "MINI")) | ||||
|                         { | ||||
|                             type = MINI; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                     else if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5")) | ||||
|                     else if ((curr->vendor->name == "Creality") && (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values)) | ||||
|                     { | ||||
|                         type = MK3; | ||||
|                         break; | ||||
|                     } | ||||
|                     else if (boost::contains(curr->name, "MK2")) | ||||
|                     { | ||||
|                         type = MK2; | ||||
|                         break; | ||||
|                         if (boost::contains(curr->name, "ENDER-3")) | ||||
|                         { | ||||
|                             type = ENDER3; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  |  | |||
|  | @ -67,6 +67,8 @@ public: | |||
|         MK2, | ||||
|         MK3, | ||||
|         SL1, | ||||
|         MINI, | ||||
|         ENDER3, | ||||
|         Custom, | ||||
|         Num_Types | ||||
|     }; | ||||
|  |  | |||
|  | @ -707,24 +707,24 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M | |||
|     print_volume.min(2) = -1e10; | ||||
| 
 | ||||
|     ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside; | ||||
|     bool all_contained = true; | ||||
| 
 | ||||
|     bool contained_min_one = false; | ||||
| 
 | ||||
|     for (GLVolume* volume : this->volumes) | ||||
|     { | ||||
|         if ((volume == nullptr) || !volume->printable || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled)) | ||||
|         if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled)) | ||||
|             continue; | ||||
| 
 | ||||
|         const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); | ||||
|         bool contained = print_volume.contains(bb); | ||||
|         all_contained &= contained; | ||||
| 
 | ||||
|         volume->is_outside = !contained; | ||||
|         if (!volume->printable) | ||||
|             continue; | ||||
| 
 | ||||
|         if (contained) | ||||
|             contained_min_one = true; | ||||
| 
 | ||||
|         volume->is_outside = !contained; | ||||
| 
 | ||||
|         if ((state == ModelInstance::PVS_Inside) && volume->is_outside) | ||||
|             state = ModelInstance::PVS_Fully_Outside; | ||||
| 
 | ||||
|  | @ -735,7 +735,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M | |||
|     if (out_state != nullptr) | ||||
|         *out_state = state; | ||||
| 
 | ||||
|     return /*all_contained*/ contained_min_one; // #ys_FIXME_delete_after_testing
 | ||||
|     return contained_min_one; | ||||
| } | ||||
| 
 | ||||
| void GLVolumeCollection::reset_outside_state() | ||||
|  |  | |||
|  | @ -261,11 +261,7 @@ bool BackgroundSlicingProcess::start() | |||
| 	if (m_state == STATE_INITIAL) { | ||||
| 		// The worker thread is not running yet. Start it.
 | ||||
| 		assert(! m_thread.joinable()); | ||||
| 		boost::thread::attributes attrs; | ||||
| 		// Duplicating the stack allocation size of Thread Building Block worker threads of the thread pool:
 | ||||
| 		// allocate 4MB on a 64bit system, allocate 2MB on a 32bit system by default.
 | ||||
| 		attrs.set_stack_size((sizeof(void*) == 4) ? (2048 * 1024) : (4096 * 1024)); | ||||
| 		m_thread = boost::thread(attrs, [this]{this->thread_proc_safe();}); | ||||
| 		m_thread = create_thread([this]{this->thread_proc_safe();}); | ||||
| 		// Wait until the worker thread is ready to execute the background processing task.
 | ||||
| 		m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); | ||||
| 	} | ||||
|  |  | |||
|  | @ -6,12 +6,12 @@ | |||
| #include <mutex> | ||||
| 
 | ||||
| #include <boost/filesystem.hpp> | ||||
| #include <boost/thread.hpp> | ||||
| 
 | ||||
| #include <wx/event.h> | ||||
| 
 | ||||
| #include "libslic3r/Print.hpp" | ||||
| #include "slic3r/Utils/PrintHost.hpp" | ||||
| #include "slic3r/Utils/Thread.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ | |||
| 
 | ||||
| #include "boost/nowide/iostream.hpp" | ||||
| #include <boost/algorithm/string/predicate.hpp> | ||||
| #include <boost/filesystem.hpp> | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
|  | @ -60,7 +61,9 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf | |||
| { | ||||
|     m_shape = default_pt.values; | ||||
|     m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value; | ||||
|     std::replace(m_custom_texture.begin(), m_custom_texture.end(), '\\', '/'); | ||||
|     m_custom_model = custom_model.value.empty() ? NONE : custom_model.value; | ||||
|     std::replace(m_custom_model.begin(), m_custom_model.end(), '\\', '/'); | ||||
| 
 | ||||
|     auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape"))); | ||||
|     sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font()); | ||||
|  | @ -212,7 +215,18 @@ wxPanel* BedShapePanel::init_texture_panel() | |||
|                 wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject()); | ||||
|                 if (lbl != nullptr) | ||||
|                 { | ||||
|                     wxString tooltip_text = (m_custom_texture == NONE) ? "" : _(m_custom_texture); | ||||
|                     bool exists = (m_custom_texture == NONE) || boost::filesystem::exists(m_custom_texture); | ||||
|                     lbl->SetForegroundColour(exists ? wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) : wxColor(*wxRED)); | ||||
| 
 | ||||
|                     wxString tooltip_text = ""; | ||||
|                     if (m_custom_texture != NONE) | ||||
|                     { | ||||
|                         if (!exists) | ||||
|                             tooltip_text += _(L("Not found: ")); | ||||
| 
 | ||||
|                         tooltip_text += _(m_custom_texture); | ||||
|                     } | ||||
| 
 | ||||
|                     wxToolTip* tooltip = lbl->GetToolTip(); | ||||
|                     if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text)) | ||||
|                         lbl->SetToolTip(tooltip_text); | ||||
|  | @ -280,7 +294,18 @@ wxPanel* BedShapePanel::init_model_panel() | |||
|                 wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject()); | ||||
|                 if (lbl != nullptr) | ||||
|                 { | ||||
|                     wxString tooltip_text = (m_custom_model == NONE) ? "" : _(m_custom_model); | ||||
|                     bool exists = (m_custom_model == NONE) || boost::filesystem::exists(m_custom_model); | ||||
|                     lbl->SetForegroundColour(exists ? wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) : wxColor(*wxRED)); | ||||
| 
 | ||||
|                     wxString tooltip_text = ""; | ||||
|                     if (m_custom_model != NONE) | ||||
|                     { | ||||
|                         if (!exists) | ||||
|                             tooltip_text += _(L("Not found: ")); | ||||
| 
 | ||||
|                         tooltip_text += _(m_custom_model); | ||||
|                     } | ||||
| 
 | ||||
|                     wxToolTip* tooltip = lbl->GetToolTip(); | ||||
|                     if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text)) | ||||
|                         lbl->SetToolTip(tooltip_text); | ||||
|  | @ -521,6 +546,8 @@ void BedShapePanel::load_texture() | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     std::replace(file_name.begin(), file_name.end(), '\\', '/'); | ||||
| 
 | ||||
|     wxBusyCursor wait; | ||||
| 
 | ||||
|     m_custom_texture = file_name; | ||||
|  | @ -544,6 +571,8 @@ void BedShapePanel::load_model() | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     std::replace(file_name.begin(), file_name.end(), '\\', '/'); | ||||
| 
 | ||||
|     wxBusyCursor wait; | ||||
| 
 | ||||
|     m_custom_model = file_name; | ||||
|  |  | |||
|  | @ -166,6 +166,8 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt | |||
|     int max_row_width = 0; | ||||
|     int current_row_width = 0; | ||||
| 
 | ||||
|     bool is_variants = false; | ||||
| 
 | ||||
|     for (const auto &model : models) { | ||||
|         if (! filter(model)) { continue; } | ||||
| 
 | ||||
|  | @ -220,6 +222,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt | |||
|                 auto *alt_label = new wxStaticText(variants_panel, wxID_ANY, _(L("Alternate nozzles:"))); | ||||
|                 alt_label->SetFont(font_alt_nozzle); | ||||
|                 variants_sizer->Add(alt_label, 0, wxBOTTOM, 3); | ||||
|                 is_variants = true; | ||||
|             } | ||||
| 
 | ||||
|             auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name); | ||||
|  | @ -280,10 +283,10 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt | |||
|     } | ||||
|     title_sizer->AddStretchSpacer(); | ||||
| 
 | ||||
|     if (titles.size() > 1) { | ||||
|     if (/*titles.size() > 1*/is_variants) { | ||||
|         // It only makes sense to add the All / None buttons if there's multiple printers
 | ||||
| 
 | ||||
|         auto *sel_all_std = new wxButton(this, wxID_ANY, _(L("All standard"))); | ||||
|         auto *sel_all_std = new wxButton(this, wxID_ANY, titles.size() > 1 ? _(L("All standard")) : _(L("Standard"))); | ||||
|         auto *sel_all = new wxButton(this, wxID_ANY, _(L("All"))); | ||||
|         auto *sel_none = new wxButton(this, wxID_ANY, _(L("None"))); | ||||
|         sel_all_std->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true, false); }); | ||||
|  |  | |||
|  | @ -11,6 +11,12 @@ | |||
| #include <wx/tooltip.h> | ||||
| #include <boost/algorithm/string/predicate.hpp> | ||||
| 
 | ||||
| #ifdef __WXOSX__ | ||||
| #define wxOSX true | ||||
| #else | ||||
| #define wxOSX false | ||||
| #endif | ||||
| 
 | ||||
| namespace Slic3r { namespace GUI { | ||||
| 
 | ||||
| wxString double_to_string(double const value, const int max_precision /*= 4*/) | ||||
|  | @ -304,7 +310,7 @@ void TextCtrl::BUILD() { | |||
| 	auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style); | ||||
| 	temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); | ||||
| 
 | ||||
| 	if (! m_opt.multiline) | ||||
|     if (! m_opt.multiline && !wxOSX) | ||||
| 		// Only disable background refresh for single line input fields, as they are completely painted over by the edit control.
 | ||||
| 		// This does not apply to the multi-line edit field, where the last line and a narrow frame around the text is not cleared.
 | ||||
| 		temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
|  | @ -491,7 +497,7 @@ void CheckBox::BUILD() { | |||
| 	// Set Label as a string of at least one space simbol to correct system scaling of a CheckBox 
 | ||||
| 	auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(" "), wxDefaultPosition, size);  | ||||
| 	temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); | ||||
| 	temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
| 	if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
| 	temp->SetValue(check_value); | ||||
| 	if (m_opt.readonly) temp->Disable(); | ||||
| 
 | ||||
|  | @ -601,7 +607,7 @@ void SpinCtrl::BUILD() { | |||
| 	auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, | ||||
| 		0|wxTE_PROCESS_ENTER, min_val, max_val, default_value); | ||||
| 	temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); | ||||
| 	temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
|     if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
| 
 | ||||
| // XXX: On OS X the wxSpinCtrl widget is made up of two subwidgets, unfortunatelly
 | ||||
| // the kill focus event is not propagated to the encompassing widget,
 | ||||
|  | @ -717,7 +723,7 @@ void Choice::BUILD() { | |||
|     } | ||||
| 
 | ||||
| 	temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); | ||||
| 	temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
|     if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
| 
 | ||||
| 	// recast as a wxWindow to fit the calling convention
 | ||||
| 	window = dynamic_cast<wxWindow*>(temp); | ||||
|  | @ -1072,7 +1078,7 @@ void ColourPicker::BUILD() | |||
| 
 | ||||
| 	auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size); | ||||
|     temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); | ||||
| 	temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
|     if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
| 
 | ||||
| 	// 	// recast as a wxWindow to fit the calling convention
 | ||||
| 	window = dynamic_cast<wxWindow*>(temp); | ||||
|  |  | |||
|  | @ -2094,20 +2094,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
| 
 | ||||
|         post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS,  | ||||
|                                contained_min_one && !m_model->objects.empty() && state != ModelInstance::PVS_Partly_Outside)); | ||||
| 
 | ||||
| // #ys_FIXME_delete_after_testing
 | ||||
| //         bool contained = m_volumes.check_outside_state(m_config, &state);
 | ||||
| //         if (!contained)
 | ||||
| //         {
 | ||||
| //             _set_warning_texture(WarningTexture::ObjectOutside, true);
 | ||||
| //             post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, state == ModelInstance::PVS_Fully_Outside));
 | ||||
| //         }
 | ||||
| //         else
 | ||||
| //         {
 | ||||
| //             m_volumes.reset_outside_state();
 | ||||
| //             _set_warning_texture(WarningTexture::ObjectOutside, false);
 | ||||
| //             post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, !m_model->objects.empty()));
 | ||||
| //         }
 | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|  | @ -2917,6 +2903,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) | |||
|                     volume_bbox.offset(1.0); | ||||
|                     if (volume_bbox.contains(m_mouse.scene_position)) | ||||
|                     { | ||||
|                         m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None; | ||||
|                         // The dragging operation is initiated.
 | ||||
|                         m_mouse.drag.move_volume_idx = volume_idx; | ||||
|                         m_selection.start_dragging(); | ||||
|  |  | |||
|  | @ -105,7 +105,7 @@ static void register_dpi_event() | |||
|         const auto rect = reinterpret_cast<PRECT>(lParam); | ||||
|         const wxRect wxrect(wxPoint(rect->top, rect->left), wxPoint(rect->bottom, rect->right)); | ||||
| 
 | ||||
|         DpiChangedEvent evt(EVT_DPI_CHANGED, dpi, wxrect); | ||||
|         DpiChangedEvent evt(EVT_DPI_CHANGED_SLICER, dpi, wxrect); | ||||
|         win->GetEventHandler()->AddPendingEvent(evt); | ||||
| 
 | ||||
|         return true; | ||||
|  | @ -1131,8 +1131,11 @@ void GUI_App::gcode_thumbnails_debug() | |||
|                     boost::nowide::ofstream out_file(out_filename.c_str(), std::ios::binary); | ||||
|                     if (out_file.good()) | ||||
|                     { | ||||
|                         std::string decoded = boost::beast::detail::base64_decode(row); | ||||
|                         out_file.write(decoded.c_str(), decoded.length()); | ||||
|                         std::string decoded; | ||||
|                         decoded.resize(boost::beast::detail::base64::decoded_size(row.size())); | ||||
|                         decoded.resize(boost::beast::detail::base64::decode((void*)&decoded[0], row.data(), row.size()).first); | ||||
| 
 | ||||
|                         out_file.write(decoded.c_str(), decoded.size()); | ||||
|                         out_file.close(); | ||||
|                     } | ||||
| #else | ||||
|  | @ -1147,8 +1150,11 @@ void GUI_App::gcode_thumbnails_debug() | |||
|                         std::vector<unsigned char> thumbnail(4 * width * height, 0); | ||||
|                         for (unsigned int r = 0; r < (unsigned int)rows.size(); ++r) | ||||
|                         { | ||||
|                             std::string decoded_row = boost::beast::detail::base64_decode(rows[r]); | ||||
|                             if ((unsigned int)decoded_row.length() == width * 4) | ||||
|                             std::string decoded_row; | ||||
|                             decoded_row.resize(boost::beast::detail::base64::decoded_size(rows[r].size())); | ||||
|                             decoded_row.resize(boost::beast::detail::base64::decode((void*)&decoded_row[0], rows[r].data(), rows[r].size()).first); | ||||
| 
 | ||||
|                             if ((unsigned int)decoded_row.size() == width * 4) | ||||
|                             { | ||||
|                                 void* image_ptr = (void*)(thumbnail.data() + r * width * 4); | ||||
|                                 ::memcpy(image_ptr, (const void*)decoded_row.c_str(), width * 4); | ||||
|  |  | |||
|  | @ -57,7 +57,7 @@ static wxBitmapComboBox* create_word_local_combo(wxWindow *parent) | |||
| #endif //__WXOSX__
 | ||||
| 
 | ||||
|     temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); | ||||
|     temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
|     if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
| 
 | ||||
|     temp->Append(_(L("World coordinates"))); | ||||
|     temp->Append(_(L("Local coordinates"))); | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ void on_window_geometry(wxTopLevelWindow *tlw, std::function<void()> callback) | |||
| #endif | ||||
| } | ||||
| 
 | ||||
| wxDEFINE_EVENT(EVT_DPI_CHANGED, DpiChangedEvent); | ||||
| wxDEFINE_EVENT(EVT_DPI_CHANGED_SLICER, DpiChangedEvent); | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| template<class F> typename F::FN winapi_get_function(const wchar_t *dll, const char *fn_name) { | ||||
|  |  | |||
|  | @ -50,7 +50,7 @@ struct DpiChangedEvent : public wxEvent { | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| wxDECLARE_EVENT(EVT_DPI_CHANGED, DpiChangedEvent); | ||||
| wxDECLARE_EVENT(EVT_DPI_CHANGED_SLICER, DpiChangedEvent); | ||||
| 
 | ||||
| template<class P> class DPIAware : public P | ||||
| { | ||||
|  | @ -75,7 +75,7 @@ public: | |||
| 
 | ||||
| //        recalc_font();
 | ||||
| 
 | ||||
|         this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) { | ||||
|         this->Bind(EVT_DPI_CHANGED_SLICER, [this](const DpiChangedEvent &evt) { | ||||
|             m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT; | ||||
| 
 | ||||
|             m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize(); | ||||
|  |  | |||
|  | @ -175,7 +175,7 @@ public: | |||
| 					staticbox(title!=""), extra_column(extra_clmn) { | ||||
|         if (staticbox) { | ||||
|             stb = new wxStaticBox(_parent, wxID_ANY, title); | ||||
|             stb->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
|             if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT); | ||||
|             stb->SetFont(wxGetApp().bold_font()); | ||||
|         } else | ||||
|         	stb = nullptr; | ||||
|  |  | |||
|  | @ -76,6 +76,7 @@ | |||
| #include "../Utils/PrintHost.hpp" | ||||
| #include "../Utils/FixModelByWin10.hpp" | ||||
| #include "../Utils/UndoRedo.hpp" | ||||
| #include "../Utils/Thread.hpp" | ||||
| 
 | ||||
| #include <wx/glcanvas.h>    // Needs to be last because reasons :-/
 | ||||
| #include "WipeTowerDialog.hpp" | ||||
|  | @ -87,8 +88,6 @@ using Slic3r::Preset; | |||
| using Slic3r::PrintHostJob; | ||||
| 
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| static const std::vector < std::pair<unsigned int, unsigned int>> THUMBNAIL_SIZE_FFF = { { 240, 320 }, { 220, 165 }, { 16, 16 } }; | ||||
| static const std::vector<std::pair<unsigned int, unsigned int>> THUMBNAIL_SIZE_SLA = { { 800, 480 } }; | ||||
| static const std::pair<unsigned int, unsigned int> THUMBNAIL_SIZE_3MF = { 256, 256 }; | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 
 | ||||
|  | @ -231,7 +230,7 @@ SlicedInfo::SlicedInfo(wxWindow *parent) : | |||
|     init_info_label(_(L("Used Filament (mm³)"))); | ||||
|     init_info_label(_(L("Used Filament (g)"))); | ||||
|     init_info_label(_(L("Used Material (unit)"))); | ||||
|     init_info_label(_(L("Cost"))); | ||||
|     init_info_label(_(L("Cost (money)"))); | ||||
|     init_info_label(_(L("Estimated printing time"))); | ||||
|     init_info_label(_(L("Number of tool changes"))); | ||||
| 
 | ||||
|  | @ -1129,12 +1128,10 @@ void Sidebar::show_info_sizer() | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void Sidebar::show_sliced_info_sizer(const bool show) | ||||
| void Sidebar::update_sliced_info_sizer() | ||||
| { | ||||
|     wxWindowUpdateLocker freeze_guard(this); | ||||
| 
 | ||||
|     p->sliced_info->Show(show); | ||||
|     if (show) { | ||||
|     if (p->sliced_info->IsShown(size_t(0))) | ||||
|     { | ||||
|         if (p->plater->printer_technology() == ptSLA) | ||||
|         { | ||||
|             const SLAPrintStatistics& ps = p->plater->sla_print().print_statistics(); | ||||
|  | @ -1150,7 +1147,18 @@ void Sidebar::show_sliced_info_sizer(const bool show) | |||
|                 wxString::Format("%.2f", (ps.objects_used_material + ps.support_used_material) / 1000); | ||||
|             p->sliced_info->SetTextAndShow(siMateril_unit, info_text, new_label); | ||||
| 
 | ||||
|             p->sliced_info->SetTextAndShow(siCost, "N/A"/*wxString::Format("%.2f", ps.total_cost)*/); | ||||
|             wxString str_total_cost = "N/A"; | ||||
| 
 | ||||
|             DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_SLA_MATERIAL)->get_config(); | ||||
|             if (cfg->option("bottle_cost")->getFloat() > 0.0 && | ||||
|                 cfg->option("bottle_volume")->getFloat() > 0.0) | ||||
|             { | ||||
|                 double material_cost = cfg->option("bottle_cost")->getFloat() /  | ||||
|                                        cfg->option("bottle_volume")->getFloat(); | ||||
|                 str_total_cost = wxString::Format("%.2f", material_cost*(ps.objects_used_material + ps.support_used_material) / 1000);                 | ||||
|             } | ||||
|             p->sliced_info->SetTextAndShow(siCost, str_total_cost); | ||||
| 
 | ||||
|             wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : get_time_dhms(float(ps.estimated_print_time)); | ||||
|             p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _(L("Estimated printing time")) + " :"); | ||||
| 
 | ||||
|  | @ -1224,6 +1232,15 @@ void Sidebar::show_sliced_info_sizer(const bool show) | |||
|             p->sliced_info->SetTextAndShow(siMateril_unit, "N/A"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Sidebar::show_sliced_info_sizer(const bool show) | ||||
| { | ||||
|     wxWindowUpdateLocker freeze_guard(this); | ||||
| 
 | ||||
|     p->sliced_info->Show(show); | ||||
|     if (show) | ||||
|         update_sliced_info_sizer(); | ||||
| 
 | ||||
|     Layout(); | ||||
|     p->scrolled->Refresh(); | ||||
|  | @ -1441,7 +1458,7 @@ struct Plater::priv | |||
|     class Job : public wxEvtHandler | ||||
|     { | ||||
|         int               m_range = 100; | ||||
|         std::future<void> m_ftr; | ||||
|         boost::thread     m_thread; | ||||
|         priv *            m_plater = nullptr; | ||||
|         std::atomic<bool> m_running{false}, m_canceled{false}; | ||||
|         bool              m_finalized = false; | ||||
|  | @ -1482,7 +1499,8 @@ struct Plater::priv | |||
|             // Do a full refresh of scene tree, including regenerating
 | ||||
|             // all the GLVolumes. FIXME The update function shall just
 | ||||
|             // reload the modified matrices.
 | ||||
|             if (!was_canceled()) plater().update((unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH); | ||||
|             if (!was_canceled()) | ||||
|                 plater().update(unsigned(UpdateParams::FORCE_FULL_SCREEN_REFRESH)); | ||||
|         } | ||||
| 
 | ||||
|     public: | ||||
|  | @ -1511,9 +1529,9 @@ struct Plater::priv | |||
|         } | ||||
| 
 | ||||
|         Job(const Job &) = delete; | ||||
|         Job(Job &&)      = default; | ||||
|         Job(Job &&)      = delete; | ||||
|         Job &operator=(const Job &) = delete; | ||||
|         Job &operator=(Job &&) = default; | ||||
|         Job &operator=(Job &&) = delete; | ||||
| 
 | ||||
|         virtual void process() = 0; | ||||
| 
 | ||||
|  | @ -1537,7 +1555,7 @@ struct Plater::priv | |||
|                 wxBeginBusyCursor(); | ||||
| 
 | ||||
|                 try { // Execute the job
 | ||||
|                     m_ftr = std::async(std::launch::async, &Job::run, this); | ||||
|                     m_thread = create_thread([this] { this->run(); }); | ||||
|                 } catch (std::exception &) { | ||||
|                     update_status(status_range(), | ||||
|                                   _(L("ERROR: not enough resources to " | ||||
|  | @ -1553,16 +1571,15 @@ struct Plater::priv | |||
|         // returned if the timeout has been reached and the job is still
 | ||||
|         // running. Call cancel() before this fn if you want to explicitly
 | ||||
|         // end the job.
 | ||||
|         bool join(int timeout_ms = 0) const | ||||
|         bool join(int timeout_ms = 0) | ||||
|         { | ||||
|             if (!m_ftr.valid()) return true; | ||||
| 
 | ||||
|             if (!m_thread.joinable()) return true; | ||||
|              | ||||
|             if (timeout_ms <= 0) | ||||
|                 m_ftr.wait(); | ||||
|             else if (m_ftr.wait_for(std::chrono::milliseconds( | ||||
|                          timeout_ms)) == std::future_status::timeout) | ||||
|                 m_thread.join(); | ||||
|             else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) | ||||
|                 return false; | ||||
| 
 | ||||
|              | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|  | @ -3056,14 +3073,16 @@ bool Plater::priv::restart_background_process(unsigned int state) | |||
|              (this->background_process.state() != BackgroundSlicingProcess::STATE_RUNNING)) | ||||
|         { | ||||
|             // update thumbnail data
 | ||||
|             const std::vector<Vec2d> &thumbnail_sizes = this->background_process.current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values; | ||||
|             if (this->printer_technology == ptFFF) | ||||
|             { | ||||
|                 // for ptFFF we need to generate the thumbnails before the export of gcode starts
 | ||||
|                 this->thumbnail_data.clear(); | ||||
|                 for (const std::pair<unsigned int, unsigned int>& size : THUMBNAIL_SIZE_FFF) | ||||
|                 for (const Vec2d &sized : thumbnail_sizes) | ||||
|                 { | ||||
|                     this->thumbnail_data.push_back(ThumbnailData()); | ||||
|                     generate_thumbnail(this->thumbnail_data.back(), size.first, size.second, true, true, false); | ||||
| 					Point size(sized); // round to ints
 | ||||
|                     generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false); | ||||
|                 } | ||||
|             } | ||||
|             else if (this->printer_technology == ptSLA) | ||||
|  | @ -3071,10 +3090,11 @@ bool Plater::priv::restart_background_process(unsigned int state) | |||
|                 // for ptSLA generate thumbnails without supports and pad (not yet calculated)
 | ||||
|                 // to render also supports and pad see on_slicing_update()
 | ||||
|                 this->thumbnail_data.clear(); | ||||
|                 for (const std::pair<unsigned int, unsigned int>& size : THUMBNAIL_SIZE_SLA) | ||||
|                 for (const Vec2d &sized : thumbnail_sizes) | ||||
|                 { | ||||
|                     this->thumbnail_data.push_back(ThumbnailData()); | ||||
|                     generate_thumbnail(this->thumbnail_data.back(), size.first, size.second, true, true, false); | ||||
| 					Point size(sized); // round to ints
 | ||||
| 					generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -3426,11 +3446,13 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) | |||
|         // for ptSLA generate the thumbnail after supports and pad have been calculated to have them rendered
 | ||||
|         if ((this->printer_technology == ptSLA) && (evt.status.percent == -3)) | ||||
|         { | ||||
|             const std::vector<Vec2d>& thumbnail_sizes = this->background_process.current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values; | ||||
|             this->thumbnail_data.clear(); | ||||
|             for (const std::pair<unsigned int, unsigned int>& size : THUMBNAIL_SIZE_SLA) | ||||
|             for (const Vec2d &sized : thumbnail_sizes) | ||||
|             { | ||||
|                 this->thumbnail_data.push_back(ThumbnailData()); | ||||
|                 generate_thumbnail(this->thumbnail_data.back(), size.first, size.second, true, false, false); | ||||
|                 Point size(sized); // round to ints
 | ||||
|                 generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, false, false); | ||||
|             } | ||||
|         } | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|  |  | |||
|  | @ -113,6 +113,7 @@ public: | |||
|     void                    update_objects_list_extruder_column(size_t extruders_count); | ||||
|     void                    show_info_sizer(); | ||||
|     void                    show_sliced_info_sizer(const bool show); | ||||
|     void                    update_sliced_info_sizer(); | ||||
|     void                    enable_buttons(bool enable); | ||||
|     void                    set_btn_label(const ActionButtonType btn_type, const wxString& label) const; | ||||
|     bool                    show_reslice(bool show) const; | ||||
|  |  | |||
|  | @ -403,7 +403,7 @@ const std::vector<std::string>& Preset::print_options() | |||
|         "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", | ||||
|         "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", | ||||
|         "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "single_extruder_multi_material_priming", | ||||
|         "compatible_printers", "compatible_printers_condition", "inherits" | ||||
|         "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits" | ||||
|     }; | ||||
|     return s_opts; | ||||
| } | ||||
|  | @ -444,7 +444,8 @@ const std::vector<std::string>& Preset::printer_options() | |||
|             "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e", | ||||
|             "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e", | ||||
|             "machine_min_extruding_rate", "machine_min_travel_rate", | ||||
|             "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e" | ||||
|             "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e", | ||||
|             "thumbnails" | ||||
|         }; | ||||
|         s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); | ||||
|     } | ||||
|  | @ -513,6 +514,10 @@ const std::vector<std::string>& Preset::sla_material_options() | |||
|         s_opts = { | ||||
|             "material_type", | ||||
|             "initial_layer_height", | ||||
|             "bottle_cost", | ||||
|             "bottle_volume", | ||||
|             "bottle_weight", | ||||
|             "material_density", | ||||
|             "exposure_time", | ||||
|             "initial_exposure_time", | ||||
|             "material_correction", | ||||
|  |  | |||
|  | @ -96,6 +96,7 @@ PresetBundle::PresetBundle() : | |||
|         preset.config.optptr("printer_vendor", true); | ||||
|         preset.config.optptr("printer_model", true); | ||||
|         preset.config.optptr("printer_variant", true); | ||||
| 		preset.config.optptr("thumbnails", true); | ||||
|         if (i == 0) { | ||||
|             preset.config.optptr("default_print_profile", true); | ||||
|             preset.config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" }; | ||||
|  |  | |||
|  | @ -1170,6 +1170,7 @@ void TabPrint::build() | |||
|         optgroup->append_single_option_line("wipe_tower_width"); | ||||
|         optgroup->append_single_option_line("wipe_tower_rotation_angle"); | ||||
|         optgroup->append_single_option_line("wipe_tower_bridging"); | ||||
|         optgroup->append_single_option_line("wipe_tower_no_sparse_layers"); | ||||
|         optgroup->append_single_option_line("single_extruder_multi_material_priming"); | ||||
| 
 | ||||
|         optgroup = page->new_optgroup(_(L("Advanced"))); | ||||
|  | @ -3419,8 +3420,41 @@ void TabSLAMaterial::build() | |||
| 
 | ||||
|     auto page = add_options_page(_(L("Material")), "resin"); | ||||
| 
 | ||||
|     auto optgroup = page->new_optgroup(_(L("Layers"))); | ||||
| //     optgroup->append_single_option_line("layer_height");
 | ||||
|     auto optgroup = page->new_optgroup(_(L("Material"))); | ||||
|     optgroup->append_single_option_line("bottle_cost"); | ||||
|     optgroup->append_single_option_line("bottle_volume"); | ||||
|     optgroup->append_single_option_line("bottle_weight"); | ||||
|     optgroup->append_single_option_line("material_density"); | ||||
| 
 | ||||
|     optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) | ||||
|     { | ||||
|         DynamicPrintConfig new_conf = *m_config; | ||||
| 
 | ||||
|         if (opt_key == "bottle_volume") { | ||||
|             double new_bottle_weight =  boost::any_cast<double>(value)/(new_conf.option("material_density")->getFloat() * 1000); | ||||
|             new_conf.set_key_value("bottle_weight", new ConfigOptionFloat(new_bottle_weight)); | ||||
|         } | ||||
|         if (opt_key == "bottle_weight") { | ||||
|             double new_bottle_volume =  boost::any_cast<double>(value)*(new_conf.option("material_density")->getFloat() * 1000); | ||||
|             new_conf.set_key_value("bottle_volume", new ConfigOptionFloat(new_bottle_volume)); | ||||
|         } | ||||
|         if (opt_key == "material_density") { | ||||
|             double new_bottle_volume = new_conf.option("bottle_weight")->getFloat() * boost::any_cast<double>(value) * 1000; | ||||
|             new_conf.set_key_value("bottle_volume", new ConfigOptionFloat(new_bottle_volume)); | ||||
|         } | ||||
| 
 | ||||
|         load_config(new_conf); | ||||
| 
 | ||||
|         update_dirty(); | ||||
|         on_value_change(opt_key, value); | ||||
| 
 | ||||
|         if (opt_key == "bottle_volume" || opt_key == "bottle_cost") { | ||||
|             wxGetApp().sidebar().update_sliced_info_sizer(); | ||||
|             wxGetApp().sidebar().Layout(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     optgroup = page->new_optgroup(_(L("Layers"))); | ||||
|     optgroup->append_single_option_line("initial_layer_height"); | ||||
| 
 | ||||
|     optgroup = page->new_optgroup(_(L("Exposure"))); | ||||
|  |  | |||
							
								
								
									
										28
									
								
								src/slic3r/Utils/Thread.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/slic3r/Utils/Thread.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| #ifndef THREAD_HPP | ||||
| #define THREAD_HPP | ||||
| 
 | ||||
| #include <utility> | ||||
| #include <boost/thread.hpp> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| template<class Fn> | ||||
| inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn) | ||||
| { | ||||
|     // Duplicating the stack allocation size of Thread Building Block worker
 | ||||
|     // threads of the thread pool: allocate 4MB on a 64bit system, allocate 2MB
 | ||||
|     // on a 32bit system by default.
 | ||||
|      | ||||
|     attrs.set_stack_size((sizeof(void*) == 4) ? (2048 * 1024) : (4096 * 1024)); | ||||
|     return boost::thread{attrs, std::forward<Fn>(fn)}; | ||||
| } | ||||
| 
 | ||||
| template<class Fn> inline boost::thread create_thread(Fn &&fn) | ||||
| { | ||||
|     boost::thread::attributes attrs; | ||||
|     return create_thread(attrs, std::forward<Fn>(fn));     | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // THREAD_HPP
 | ||||
|  | @ -220,8 +220,232 @@ static ExPolygon vase_with_fins() | |||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| static ExPolygon contour_with_hole() | ||||
| { | ||||
| 	ExPolygon out; | ||||
| 	out.contour.points = { | ||||
| 		{ 23302819, 108248}, { 23410179, 157624}, { 23451825, 176777}, { 24106418, 478750}, { 24704172, 811512}, { 24883849, 911534}, { 25980045, 1530217}, { 26591038, 1897423}, { 26829981, 2041022}, { 27158523, 2249848}, { 27618921, 2584465},  | ||||
| 		{ 27896903, 2786507}, { 28144524, 2978990}, { 28815685, 3551061}, { 28909975, 3628821}, { 29371498, 4009409}, { 29402087, 4037084}, { 29493584, 4119861}, { 29765627, 4382947}, { 30607836, 5197449}, { 30934687, 5508413}, { 31019374, 5593546},  | ||||
| 		{ 31075807, 5655861}, { 31235879, 5823254}, { 31667505, 6274618}, { 31976596, 6656087}, { 32328364, 7055603}, { 32440973, 7183484}, { 32491346, 7249288}, { 33179667, 8148478}, { 33575401, 8717521}, { 33835875, 9075811}, { 34010014, 9315332},  | ||||
| 		{ 34304500, 9781688}, { 34369165, 9898535}, { 34397842, 9950359}, { 34494651, 10316439}, { 34501993, 10344190}, { 34385828, 10617514}, { 34331252, 10651174}, { 34084812, 10803186}, { 33894353, 10899665}, { 33398927, 11326583},  | ||||
| 		{ 33183121, 11494200}, { 32195826, 12261037}, { 31686925, 12719913}, { 31571718, 12807396}, { 31250995, 13050935}, { 31207108, 13086856}, { 31130381, 13149671}, { 31070741, 13206732}, { 30967095, 13305896}, { 30228082, 14071658},  | ||||
| 		{ 30116771, 14212337}, { 30044101, 14304176}, { 29567520, 14906137}, { 29043350, 15664879}, { 28911161, 15871189}, { 28855871, 15957479}, { 28714334, 16227582}, { 28650159, 16350050}, { 28364584, 16899765}, { 28240857, 17235607},  | ||||
| 		{ 28151371, 17509658}, { 28114198, 17623503}, { 28309361, 17730441}, { 28370394, 17763884}, { 28488974, 17847025}, { 28525745, 17872806}, { 29082248, 18281292}, { 29152930, 18376480}, { 29168058, 18396855}, { 29173722, 18656366},  | ||||
| 		{ 29176206, 18770149}, { 29167406, 18857292}, { 29104337, 19029141}, { 29049428, 19178752}, { 28907061, 19434701}, { 28857790, 19523283}, { 28715480, 19775043}, { 28630622, 20043684}, { 28609342, 20111052}, { 28573760, 20267045},  | ||||
| 		{ 28403454, 21103762}, { 28370165, 21230085}, { 28332310, 21373746}, { 28315057, 21418891}, { 28294569, 21472487}, { 28334157, 21579715}, { 28561468, 21814880}, { 28854906, 22118451}, { 29225599, 22499341}, { 29285205, 22617454},  | ||||
| 		{ 29324833, 22695983}, { 29313473, 22800767}, { 29312583, 22808982}, { 29272380, 22876835}, { 28829469, 23460472}, { 28817999, 23488286}, { 28796393, 23540675}, { 28775618, 23627381}, { 28732328, 23808034}, { 28661140, 24177335},  | ||||
| 		{ 28645731, 24834289}, { 28625222, 25202417}, { 28579034, 26031478}, { 28586310, 26420529}, { 28633240, 26560504}, { 28664456, 26653603}, { 28740916, 26788014}, { 28797005, 26886614}, { 28812464, 26950783}, { 28858428, 27009579},  | ||||
| 		{ 28975940, 26859631}, { 29022419, 26805440}, { 29115451, 26696972}, { 29135739, 26685915}, { 29155135, 26675346}, { 29408332, 26616458}, { 29592642, 26573591}, { 29614928, 26568091}, { 29711634, 26559197}, { 30723503, 26466299},  | ||||
| 		{ 31183646, 26470661}, { 31550568, 26550771}, { 31777556, 26600329}, { 32014697, 26671604}, { 32334931, 26854665}, { 32449353, 26920987}, { 32657873, 27041843}, { 32701539, 27084927}, { 32750872, 27133602}, { 33434549, 27790306},  | ||||
| 		{ 33487600, 27817659}, { 33548673, 27849142}, { 33793150, 28109624}, { 33877574, 28164293}, { 33965395, 28221161}, { 33999067, 28249986}, { 34024398, 28271673}, { 34059690, 28329572}, { 34087359, 28374972}, { 34181544, 28710471},  | ||||
| 		{ 34170186, 28732578}, { 34134947, 28801161}, { 34092867, 29064916}, { 33950784, 29233310}, { 33878646, 29318804}, { 33721956, 29672399}, { 33660358, 29727949}, { 33620108, 29764243}, { 33393624, 30270577}, { 33094597, 30771032},  | ||||
| 		{ 33063116, 30812704}, { 32973928, 30930779}, { 32608081, 31341847}, { 32393317, 31544017}, { 32206520, 31719862}, { 31997581, 31894374}, { 31972538, 31942583}, { 32059002, 32025240}, { 32171917, 32133182}, { 32501317, 32311025},  | ||||
| 		{ 32715593, 32426714}, { 32802065, 32479231}, { 32956210, 32574312}, { 33249042, 32770899}, { 33946833, 33239350}, { 34445301, 33680139}, { 34778020, 33974357}, { 35230994, 34391224}, { 35341113, 34460366}, { 35450459, 34529022},  | ||||
| 		{ 35625170, 34673345}, { 35764733, 34757179}, { 35775747, 34633947}, { 35846476, 34564107}, { 35965365, 34446723}, { 36038088, 34379954}, { 36151170, 34276133}, { 36426218, 34106680}, { 36531666, 34187969}, { 36695885, 34314565},  | ||||
| 		{ 37011093, 34586835}, { 37067557, 34150814}, { 37052506, 33989541}, { 37037043, 33823855}, { 37069574, 33661923}, { 37083653, 33591851}, { 37186706, 33497192}, { 37521634, 33288703}, { 37617140, 33275082}, { 37684699, 33219614},  | ||||
| 		{ 37821418, 33228393}, { 37938489, 33235910}, { 38091617, 33138918}, { 38155158, 33060873}, { 38213556, 32989142}, { 38727086, 32659362}, { 38746459, 32654507}, { 38809135, 32638806}, { 38820634, 32624462}, { 38855007, 32581573},  | ||||
| 		{ 39134002, 32235481}, { 39392850, 32163442}, { 39569189, 32115608}, { 39686862, 32083692}, { 39744314, 32146839}, { 39840707, 31963655}, { 39973169, 31711932}, { 40025735, 31592644}, { 40157184, 31465080}, { 40313010, 31313863},  | ||||
| 		{ 40390192, 31223588}, { 40418596, 31230809}, { 40594404, 31186692}, { 40732045, 31068306}, { 40746151, 30846139}, { 40761255, 30608300}, { 40853394, 30223426}, { 40876768, 30095588}, { 40895496, 29993166}, { 40968240, 29949606},  | ||||
| 		{ 41197066, 29989787}, { 41412367, 30027591}, { 41472384, 29977101}, { 41695297, 29659954}, { 41890516, 29382211}, { 42157410, 28987811}, { 42408947, 28616097}, { 42669462, 28292349}, { 42683144, 28275345}, { 42919982, 27924149},  | ||||
| 		{ 43162781, 27628506}, { 43527344, 27260325}, { 43847191, 27036250}, { 44057061, 26922424}, { 44231096, 26828037}, { 44301999, 26795490}, { 44327421, 26804561}, { 44319287, 26913761}, { 44143507, 27648484}, { 44107324, 27729499},  | ||||
| 		{ 44074236, 27803580}, { 44025541, 27932083}, { 43944121, 28146941}, { 43877811, 28710269}, { 43895199, 28764671}, { 43933238, 28883702}, { 43919165, 29004140}, { 43888109, 29269841}, { 43825852, 29576752}, { 43811824, 29609468},  | ||||
| 		{ 43748820, 29756420}, { 43763658, 29837769}, { 43832567, 30215488}, { 44075125, 29807258}, { 44209233, 29804204}, { 44310228, 29813855}, { 44365586, 29958259}, { 43873534, 30271247}, { 44003187, 30330249}, { 44617279, 30687869},  | ||||
| 		{ 44694113, 31070182}, { 44941015, 31257544}, { 45130334, 31171398}, { 45147836, 31132029}, { 45242053, 31070592}, { 45345637, 31033061}, { 45565937, 30953238}, { 45609517, 30857448}, { 45651888, 30764320}, { 45660681, 30754094},  | ||||
| 		{ 45822750, 30772646}, { 45944979, 30753042}, { 45964326, 30749938}, { 46054945, 30795588}, { 46577640, 31130668}, { 46870296, 31313313}, { 46976414, 31379541}, { 46998128, 31406087}, { 47008874, 31439291}, { 47031018, 31569281},  | ||||
| 		{ 47031214, 31576854}, { 47036334, 31774677}, { 47193705, 31889293}, { 47353245, 32029772}, { 47484683, 32145510}, { 47534251, 32233847}, { 47538509, 32241438}, { 47602626, 32453825}, { 47622648, 32465115}, { 47701707, 32575250},  | ||||
| 		{ 47776955, 33122018}, { 47677092, 33345574}, { 47630772, 33380015}, { 47572757, 33423150}, { 47328653, 33537512}, { 47343826, 33612940}, { 47462219, 33617810}, { 47578431, 33622591}, { 47808035, 33604884}, { 47842258, 33885890},  | ||||
| 		{ 47847000, 34154765}, { 47852298, 34455418}, { 47806556, 34798342}, { 47804979, 34803470}, { 47795265, 34835122}, { 47811501, 34879922}, { 47843100, 35247684}, { 47839663, 35481904}, { 47833503, 35902474}, { 47803910, 36044010},  | ||||
| 		{ 47819598, 36077879}, { 47841934, 36100587}, { 47854870, 36165755}, { 47911856, 36452861}, { 47927332, 36616382}, { 47936929, 36717785}, { 47770423, 36987292}, { 47699764, 37101659}, { 47671115, 37157488}, { 47423375, 37424772},  | ||||
| 		{ 47616349, 37518717}, { 47680621, 37550006}, { 47836151, 37632587}, { 47811936, 37777743}, { 47716954, 38113916}, { 47654340, 38250491}, { 47533407, 38514290}, { 47431515, 38674036}, { 47367427, 38987733}, { 47348164, 39043625},  | ||||
| 		{ 47298533, 39187606}, { 47279676, 39231940}, { 47252411, 39296047}, { 47246894, 39304927}, { 47238746, 39318037}, { 47232029, 39335258}, { 47220194, 39365593}, { 47196053, 39429922}, { 47159408, 39527585}, { 47041654, 39691835},  | ||||
| 		{ 47002148, 39908798}, { 46964248, 39997937}, { 46895728, 40159083}, { 46826610, 40301043}, { 46763479, 40430710}, { 46514929, 40884923}, { 46474179, 40918994}, { 46440818, 40946888}, { 46433233, 40992821}, { 46426528, 41033401},  | ||||
| 		{ 46108271, 41626808}, { 46056215, 41723876}, { 45997871, 41855066}, { 45755987, 42227269}, { 45653183, 42385466}, { 45444848, 42652871}, { 45380966, 42654262}, { 45336326, 42655238}, { 45326382, 42763461}, { 45318953, 42844333},  | ||||
| 		{ 45175146, 43086382}, { 45086585, 43235443}, { 45055897, 43281060}, { 44968051, 43418247}, { 44470500, 44195272}, { 44413430, 44364401}, { 44390221, 44433179}, { 44309502, 44528273}, { 44199667, 44604532}, { 43887229, 44833256},  | ||||
| 		{ 43815081, 44886070}, { 43726552, 44932547}, { 43689058, 44928887}, { 43686137, 44927822}, { 43280111, 44871367}, { 43249704, 44937548}, { 43324977, 45004000}, { 43046101, 45224515}, { 42898716, 45341059}, { 42838343, 45382240},  | ||||
| 		{ 42721108, 45493632}, { 42470119, 45669357}, { 42359756, 45746630}, { 42073412, 45910212}, { 42022050, 45926905}, { 41907133, 46027394}, { 41144940, 46559849}, { 40902566, 46683907}, { 40884989, 46688481}, { 40811763, 46707548},  | ||||
| 		{ 40768612, 46786655}, { 40675645, 46871372}, { 40548269, 46985681}, { 40382460, 47085920}, { 40082094, 47267510}, { 39768380, 47413990}, { 39734614, 47420931}, { 39586801, 47437916}, { 39408498, 47458403}, { 39355630, 47574767},  | ||||
| 		{ 39281498, 47737937}, { 39251009, 47783502}, { 39152882, 47890727}, { 39013408, 48043132}, { 38921577, 48100514}, { 38896008, 48108330}, { 38727116, 48102492}, { 38692428, 48101294}, { 38425261, 48075982}, { 38342344, 48047392},  | ||||
| 		{ 38336010, 48154957}, { 38151978, 48395628}, { 37811687, 48488990}, { 37804084, 48490379}, { 37674998, 48513979}, { 37674196, 48513196}, { 37658712, 48498074}, { 37592273, 48482371}, { 37336907, 48659173}, { 37140701, 48741338},  | ||||
| 		{ 37129466, 48764064}, { 37075599, 48873013}, { 36739574, 48838715}, { 36721697, 48864552}, { 36456161, 49171298}, { 36442740, 49184060}, { 36436660, 49212679}, { 36300951, 49585030}, { 36223897, 49727927}, { 36150156, 49864671},  | ||||
| 		{ 35924446, 50245885}, { 35769083, 50508275}, { 35750118, 50514284}, { 35323137, 50653609}, { 34050908, 50703703}, { 33864494, 50706292}, { 33666152, 50709051}, { 33813201, 50839130}, { 33884905, 50893350}, { 33912037, 50913867},  | ||||
| 		{ 34282238, 51132740}, { 35016181, 51605972}, { 35027459, 51615787}, { 35030754, 51618656}, { 35108803, 51693454}, { 35137469, 51720927}, { 34948522, 51872654}, { 34658613, 52064227}, { 34464997, 52192175}, { 34289189, 52285353},  | ||||
| 		{ 34219119, 52312637}, { 33847969, 52428212}, { 33681538, 52480036}, { 33407178, 52510887}, { 33421683, 52685666}, { 33428342, 52765908}, { 33392094, 53146294}, { 33371466, 53362761}, { 33253040, 54291767}, { 33196142, 54612534},  | ||||
| 		{ 33128154, 54815569}, { 33095559, 54912904}, { 32570427, 55111061}, { 32525706, 55125923}, { 32458612, 55148214}, { 32385063, 55163161}, { 32282016, 55184108}, { 32241393, 55188603}, { 32190544, 55194226}, { 32027959, 55217259},  | ||||
| 		{ 32011561, 56072729}, { 32003567, 57064095}, { 31997637, 57799631}, { 32015577, 60287161}, { 32014290, 61201940}, { 32012996, 62120667}, { 32007630, 62197246}, { 32002828, 62265761}, { 32003310, 62373952}, { 32003630, 62444825},  | ||||
| 		{ 31951202, 63100419}, { 31935103, 63301732}, { 31937490, 63354807}, { 31968533, 64124669}, { 32071989, 64767136}, { 32091323, 64947492}, { 32101518, 65042609}, { 32140486, 65216353}, { 32159835, 65302616}, { 32422071, 66001036},  | ||||
| 		{ 32441049, 66056128}, { 32463003, 66119864}, { 32483582, 66164217}, { 32504016, 66208251}, { 32702117, 66557895}, { 32734168, 66611648}, { 32759723, 66654509}, { 32985249, 66546464}, { 33208649, 66439436}, { 33424955, 66330151},  | ||||
| 		{ 33554797, 66263457}, { 33891385, 66090564}, { 34622897, 65616004}, { 34819546, 65471063}, { 34988926, 65346218}, { 35739513, 64794843}, { 36421629, 64150515}, { 36944662, 63656452}, { 36959929, 63643292}, { 36964174, 63639854},  | ||||
| 		{ 36973615, 63630686}, { 37023366, 63597643}, { 37652255, 63172287}, { 37804320, 63100590}, { 37939211, 63174238}, { 37949998, 63178562}, { 38147664, 63257792}, { 38147652, 63269386}, { 38147521, 63403665}, { 38150429, 63418056},  | ||||
| 		{ 38177182, 63550576}, { 38159827, 64298859}, { 38153585, 64520174}, { 38146482, 64771937}, { 38142126, 64820836}, { 38138239, 64839298}, { 38115242, 65010431}, { 38113231, 65025393}, { 37912271, 66372984}, { 37841830, 66687479},  | ||||
| 		{ 37674277, 67228175}, { 37551047, 67593509}, { 37497098, 67727333}, { 37392268, 67951311}, { 36986640, 68817980}, { 36604483, 69575518}, { 36479686, 69769345}, { 36265058, 70102690}, { 36332308, 70163400}, { 36398395, 70223058},  | ||||
| 		{ 36718105, 70645723}, { 36714573, 70708131}, { 36707947, 70825274}, { 36665865, 71083146}, { 36295751, 71910509}, { 36243731, 72020144}, { 36010145, 72512434}, { 35364761, 74115820}, { 35327445, 74206370}, { 35287332, 74303707},  | ||||
| 		{ 35262905, 74370595}, { 35235816, 74444782}, { 35006275, 75142899}, { 34758612, 75896141}, { 34609479, 76324076}, { 34534936, 76598593}, { 34419529, 77019735}, { 34125782, 78091675}, { 34270135, 78023153}, { 34366481, 77977415},  | ||||
| 		{ 34669421, 77827427}, { 35532698, 77282412}, { 35875762, 77065829}, { 35924952, 77041288}, { 35981906, 77004141}, { 36227708, 76899428}, { 36700108, 76693284}, { 36835857, 76657801}, { 36942059, 76731654}, { 36959107, 76741135},  | ||||
| 		{ 37155031, 76850094}, { 37152161, 76868751}, { 37133420, 76990662}, { 37135224, 77014721}, { 37144331, 77136260}, { 37029215, 77783623}, { 36994547, 77984972}, { 36957442, 78200506}, { 36949745, 78231593}, { 36945059, 78243379},  | ||||
| 		{ 36909925, 78358183}, { 36908693, 78362210}, { 36517584, 79569608}, { 36400200, 79852238}, { 36160758, 80317591}, { 36001388, 80606724}, { 35929263, 80720331}, { 35803937, 80894237}, { 35313741, 81574455}, { 34810829, 82211118},  | ||||
| 		{ 34636165, 82398130}, { 34424143, 82625140}, { 34177218, 82875584}, { 34001320, 83053991}, { 33330876, 83686990}, { 33313615, 83940131}, { 33257889, 84757318}, { 33154596, 86125618}, { 33050414, 87930914}, { 33037323, 88157771},  | ||||
| 		{ 32996151, 88791902}, { 33122354, 88720953}, { 34042644, 88195810}, { 34854618, 87571171}, { 35217422, 87292077}, { 35240201, 87279017}, { 35256654, 87268145}, { 35304044, 87230648}, { 35465557, 87154377}, { 35979874, 86886707},  | ||||
| 		{ 36162994, 86833255}, { 36213131, 86859811}, { 36328089, 86920714}, { 36446386, 87103899}, { 36444792, 87129675}, { 36435583, 87278561}, { 36439166, 87306042}, { 36455346, 87430153}, { 36439626, 87577638}, { 36363937, 88287781},  | ||||
| 		{ 36334385, 88516418}, { 36324472, 88550288}, { 36266923, 88831775}, { 36258817, 88871412}, { 36009099, 90001153}, { 35925390, 90278389}, { 35742522, 90743063}, { 35584494, 91114154}, { 35511233, 91260521}, { 35378328, 91493626},  | ||||
| 		{ 34896978, 92337857}, { 34592698, 92829175}, { 34534101, 92906355}, { 34379904, 93109443}, { 34292029, 93224277}, { 34181322, 93368951}, { 33996695, 93594059}, { 33791238, 93844563}, { 33350304, 94350448}, { 32679061, 95059135},  | ||||
| 		{ 32663276, 95383974}, { 32630835, 96051559}, { 32623715, 96162432}, { 32625261, 96184173}, { 32631760, 96253789}, { 32637940, 96319986}, { 32671334, 96831435}, { 32555999, 97073603}, { 32552956, 97110111}, { 32549772, 97148299},  | ||||
| 		{ 32339278, 100576678}, { 32333722, 100685777}, { 32330348, 100752035}, { 32315766, 101012907}, { 32604225, 100816839}, { 32833219, 100661190}, { 33690734, 100037568}, { 33947721, 99810841}, { 34263306, 99532414}, { 34709871, 99161136},  | ||||
| 		{ 35458100, 98470566}, { 35535202, 98409290}, { 35673889, 98299068}, { 35825183, 98230993}, { 36031300, 98138241}, { 36364183, 98058757}, { 36389853, 98099020}, { 36443213, 98182736}, { 36495776, 98421334}, { 36464592, 98534766},  | ||||
| 		{ 36403262, 98757832}, { 36433188, 98786253}, { 36468201, 98819516}, { 36427877, 99135414}, { 36380139, 99509425}, { 36367327, 99566653}, { 36130997, 100458902}, { 36092849, 100736616}, { 35993189, 101207413}, { 35961980, 101354843},  | ||||
| 		{ 35901824, 101565944}, { 35599001, 102436249}, { 35598486, 102437494}, { 35525627, 102612717}, { 35498238, 102672427}, { 35179093, 103368204}, { 34902420, 103873765}, { 34074371, 105280790}, { 33796945, 105666257}, { 33430747, 106175067},  | ||||
| 		{ 32757675, 107021332}, { 32288404, 107611357}, { 32147333, 107782229}, { 32045181, 107903768}, { 32013865, 108446053}, { 32004365, 108597331}, { 31933356, 109727991}, { 31929556, 109801743}, { 31921205, 109963885}, { 31919950, 109998202},  | ||||
| 		{ 31917378, 110068478}, { 31935487, 110174763}, { 31962352, 110332410}, { 31868759, 110776536}, { 31779274, 112901692}, { 31772558, 113008639}, { 31763520, 113152580}, { 31760914, 113226796}, { 31757613, 113320828}, { 31878079, 113245898},  | ||||
| 		{ 32056600, 113134847}, { 32205325, 113028281}, { 32417383, 112876331}, { 32791706, 112611586}, { 33374891, 112199137}, { 34043729, 111739447}, { 34299836, 111533282}, { 34686259, 111194925}, { 35041202, 110899316}, { 36153161, 109973245},  | ||||
| 		{ 36489565, 109732139}, { 36935134, 109547251}, { 36998142, 109523782}, { 37285208, 109416845}, { 37303575, 109443686}, { 37380657, 109556352}, { 37429339, 109768662}, { 37389406, 109896075}, { 37312708, 110140778}, { 37330397, 110173101},  | ||||
| 		{ 37358669, 110224762}, { 37347970, 110508588}, { 37343682, 110622428}, { 37233824, 111039422}, { 36974286, 111866215}, { 36941457, 112104350}, { 36810462, 112600390}, { 36763361, 112778757}, { 36685333, 113003686}, { 36304140, 113929965},  | ||||
| 		{ 36303227, 113931942}, { 36219925, 114112998}, { 36185254, 114177524}, { 35766113, 114957538}, { 35699185, 115058398}, { 35271549, 115739102}, { 34529522, 116832154}, { 34230604, 117226448}, { 34152175, 117323267}, { 33753453, 117815498},  | ||||
| 		{ 33688745, 117896887}, { 33515149, 118115220}, { 33167360, 118505862}, { 32252839, 119533076}, { 31951224, 119865885}, { 31856676, 119967574}, { 31811772, 120013039}, { 31820788, 120150853}, { 31837447, 120637820}, { 31884548, 122014628},  | ||||
| 		{ 31884879, 122025348}, { 31884889, 122025915}, { 31884859, 122030715}, { 31853727, 124752378}, { 31852710, 125798379}, { 32040109, 125687330}, { 32336721, 125511560}, { 33050838, 125039566}, { 33741293, 124531865}, { 34004877, 124347492},  | ||||
| 		{ 34706008, 123857040}, { 34900746, 123695124}, { 35248769, 123405773}, { 35958009, 122839178}, { 36752647, 122139217}, { 36794698, 122103853}, { 36926183, 121993279}, { 37041929, 121900209}, { 37364281, 121641009}, { 37506782, 121535931},  | ||||
| 		{ 37599623, 121475276}, { 37805210, 121390600}, { 38274450, 121197339}, { 38429386, 121137935}, { 38611951, 121409191}, { 38647554, 121490884}, { 38558179, 121763354}, { 38544345, 121816126}, { 38504735, 121967178}, { 38540287, 122025777},  | ||||
| 		{ 38533522, 122225637}, { 38527834, 122393821}, { 38490015, 122574939}, { 38335371, 123023448}, { 38226910, 123422167}, { 38128017, 123785706}, { 38110062, 123913558}, { 38039445, 124196782}, { 37811751, 125109983}, { 37795287, 125159401},  | ||||
| 		{ 37789856, 125175267}, { 37747302, 125281671}, { 37678378, 125454008}, { 37326009, 126304036}, { 37280379, 126403545}, { 36723741, 127438116}, { 36607591, 127622339}, { 35307172, 129556108}, { 34960577, 130042788}, { 34625146, 130457962},  | ||||
| 		{ 34244496, 130929114}, { 33616736, 131638592}, { 33126427, 132192717}, { 32289044, 133098400}, { 32128210, 133254928}, { 32114672, 133265860}, { 32051379, 133303244}, { 31973610, 133349175}, { 32021296, 134019482}, { 32078232, 134927829},  | ||||
| 		{ 32192915, 136757391}, { 32250210, 137342897}, { 32301584, 137908848}, { 32406571, 139065434}, { 32456488, 139422175}, { 32511513, 139855909}, { 32587723, 140456611}, { 33065481, 140191593}, { 33323332, 140063408}, { 33766028, 139843340},  | ||||
| 		{ 33978698, 139717429}, { 34224999, 139571601}, { 35090288, 139002456}, { 36098536, 138270161}, { 36204726, 138196467}, { 36870073, 137734734}, { 36937868, 137678015}, { 37269439, 137400662}, { 38224552, 136636721}, { 39248462, 135736109},  | ||||
| 		{ 39262231, 135724978}, { 39431206, 135588270}, { 39558286, 135491389}, { 40066663, 135103831}, { 40597978, 134876486}, { 40913397, 134752602}, { 41009750, 134730971}, { 41033440, 134769160}, { 41137853, 134937472}, { 41236776, 135135656},  | ||||
| 		{ 41185372, 135392011}, { 41170368, 135466840}, { 41117848, 135629223}, { 41128977, 135726643}, { 41112112, 135925316}, { 41028443, 136275112}, { 40892177, 136737346}, { 40715316, 137337282}, { 40625973, 137862286}, { 40571054, 138077826},  | ||||
| 		{ 40413004, 138698127}, { 40307787, 139028628}, { 40280705, 139108396}, { 40108570, 139542037}, { 39781168, 140366808}, { 39776747, 140377453}, { 39771298, 140388940}, { 39694209, 140532631}, { 39126953, 141589960}, { 39112976, 141613526},  | ||||
| 		{ 38864787, 141998169}, { 38780359, 142124163}, { 37534211, 143983846}, { 36837998, 144898691}, { 36749607, 145008489}, { 36437049, 145396720}, { 36308895, 145540735}, { 35926199, 145970826}, { 35104551, 146848709}, { 34756762, 147234955},  | ||||
| 		{ 34428436, 147599589}, { 34120556, 147908106}, { 34059694, 147944671}, { 33992021, 147971830}, { 33888925, 148013197}, { 33994002, 148234139}, { 34102871, 148463060}, { 34260406, 148815390}, { 34505558, 149252538}, { 34649150, 149539737},  | ||||
| 		{ 34875213, 149991894}, { 34913367, 150060689}, { 34939834, 150108425}, { 35009188, 150222655}, { 35057146, 150301638}, { 35531716, 151039155}, { 35961908, 151607166}, { 36198106, 151919026}, { 37112008, 151466356}, { 37122527, 151461129},  | ||||
| 		{ 37143274, 151448455}, { 37793852, 151104327}, { 38753278, 150462096}, { 39057095, 150265965}, { 39387132, 150052914}, { 39992757, 149578233}, { 40209373, 149410006}, { 40448656, 149224173}, { 41648972, 148150708}, { 41827582, 147994189},  | ||||
| 		{ 42089284, 147764870}, { 42281920, 147557241}, { 42535672, 147283737}, { 43211137, 146606344}, { 43969650, 145734949}, { 44008274, 145690567}, { 44434382, 145256367}, { 44673165, 145036231}, { 44753304, 144976343}, { 44941707, 144886575},  | ||||
| 		{ 45449136, 144644796}, { 45533221, 144617860}, { 45594657, 144672684}, { 45686988, 144755077}, { 45821054, 144894151}, { 45845698, 144928928}, { 45802394, 145256827}, { 45801968, 145263145}, { 45793099, 145396327}, { 45826083, 145436911},  | ||||
| 		{ 45827387, 145448733}, { 45852550, 145676686}, { 45846396, 146183080}, { 45801072, 146729105}, { 45751200, 147329993}, { 45765306, 147565974}, { 45765766, 147690105}, { 45758629, 147823920}, { 45717918, 148587045}, { 45669293, 148998256},  | ||||
| 		{ 45657164, 149090109}, { 45565455, 149517107}, { 45390903, 150329829}, { 45380310, 150370709}, { 45303883, 150599765}, { 45049477, 151362234}, { 45041081, 151384892}, { 44988127, 151512567}, { 44899898, 151709940}, { 44188361, 153301702},  | ||||
| 		{ 43960091, 153807492}, { 43687530, 154326968}, { 43680264, 154339888}, { 43428400, 154787836}, { 43418419, 154804941}, { 43222756, 155140257}, { 43211901, 155157187}, { 43019606, 155457042}, { 42439201, 156284412}, { 42742998, 156320854},  | ||||
| 		{ 42946786, 156345296}, { 43218356, 156408139}, { 43490220, 156548626}, { 43600789, 156605776}, { 43616758, 156616967}, { 43638494, 156675797}, { 43689725, 156874320}, { 43697411, 156939181}, { 43667792, 157194800}, { 43663112, 157219786},  | ||||
| 		{ 43589483, 157612846}, { 43578259, 157650201}, { 43503908, 157897703}, { 43271842, 158586008}, { 43026656, 159112379}, { 42680049, 159768278}, { 42229097, 160621619}, { 41614538, 161818913}, { 41602009, 161838594}, { 41549009, 161921905},  | ||||
| 		{ 41366702, 162195210}, { 41089703, 162610457}, { 41051349, 162661598}, { 40028827, 163938879}, { 39981539, 163995316}, { 39859709, 164140726}, { 39557928, 164489623}, { 38840108, 165319487}, { 38817977, 165343409}, { 36508721, 167822791},  | ||||
| 		{ 35803734, 168527171}, { 35265129, 169065323}, { 35217638, 169111343}, { 35182142, 169143335}, { 34143283, 170051242}, { 34091092, 170092305}, { 33992346, 170169987}, { 32820222, 171015261}, { 32596277, 171172367}, { 32366414, 171333625},  | ||||
| 		{ 30949741, 172256683}, { 30776429, 172369214}, { 30685231, 172428426}, { 29784929, 172978028}, { 29711510, 173022900}, { 29649347, 173060901}, { 29626880, 173084470}, { 29607989, 173104288}, { 29476620, 173372906}, { 29166644, 173374167},  | ||||
| 		{ 29105869, 173396269}, { 29066168, 173410694}, { 28480959, 173773359}, { 28318456, 173874074}, { 28236958, 173920336}, { 28053468, 174015451}, { 27663961, 174212865}, { 26444009, 174781179}, { 25128636, 175292014}, { 24833691, 175404475},  | ||||
| 		{ 24567873, 175499255}, { 23673660, 175815148}, { 23263816, 175959931}, { 22989484, 175996217}, { 22919277, 176005507}, { 22821755, 176011321}, { 22593369, 175931875}, { 22197778, 175796707}, { 20895444, 175329856}, { 20562493, 175210506},  | ||||
| 		{ 20357518, 175131409}, { 19431901, 174778687}, { 19227774, 174700914}, { 17432818, 173805114}, { 17355249, 173765680}, { 17340552, 173757060}, { 17293649, 173727963}, { 15176003, 172414266}, { 14987901, 172296594}, { 14897452, 172240019},  | ||||
| 		{ 14730104, 172123866}, { 14649567, 172067971}, { 12604451, 170685425}, { 12582065, 170669040}, { 12501564, 170610143}, { 12483411, 170595498}, { 12418519, 170543227}, { 11146256, 169546467}, { 11131285, 169533173}, { 10973608, 169393198},  | ||||
| 		{ 10963368, 169383375}, { 10855356, 169279681}, { 9350332, 167783891}, { 9237755, 167663880}, { 9038028, 167450975}, { 7554140, 165846157}, { 6510331, 164717307}, { 6450301, 164645790}, { 4792198, 162599032}, { 4711896, 162499401},  | ||||
| 		{ 4702892, 162486484}, { 4689884, 162466018}, { 4689721, 162465748}, { 4111625, 161512044}, { 3262811, 159825639}, { 3085907, 159501392}, { 2964224, 159278374}, { 2880198, 159123098}, { 2827825, 159026309}, { 2730830, 158798250},  | ||||
| 		{ 2662597, 158637824}, { 2461794, 158144454}, { 2258655, 157377436}, { 2232776, 156966420}, { 2227381, 156880727}, { 2229842, 156800001}, { 2404803, 156571898}, { 2502593, 156512353}, { 2571069, 156470646}, { 3012355, 156329121},  | ||||
| 		{ 3172690, 156317433}, { 3263007, 156310852}, { 3448807, 156270050}, { 2933268, 155537448}, { 2932334, 155536119}, { 2690506, 155171633}, { 2473838, 154800417}, { 2214521, 154335871}, { 1956160, 153843466}, { 1643404, 153150964},  | ||||
| 		{ 936422, 151585583}, { 886715, 151471650}, { 881872, 151459055}, { 835673, 151315362}, { 420381, 150023686}, { 415543, 150006511}, { 411493, 149986474}, { 371105, 149740432}, { 184472, 148603483}, { 176976, 148544106}, { 143829, 148268525},  | ||||
| 		{ 141423, 148213179}, { 118798, 147692447}, { 141994, 147109270}, { 96664, 146619882}, { 46940, 146083025}, { 34028, 145778412}, { 32148, 145734124}, { 50580, 145571914}, { 79797, 145477573}, { 59893, 144996644}, { 53607, 144916874},  | ||||
| 		{ 75632, 144881102}, { 170230, 144783356}, { 367047, 144609349}, { 495089, 144649841}, { 696206, 144748339}, { 861062, 144829070}, { 1202743, 145013350}, { 1665932, 145467720}, { 1738044, 145542186}, { 1871110, 145679584}, { 2233705, 146073631},  | ||||
| 		{ 2875888, 146771504}, { 2976802, 146887761}, { 3008358, 146918708}, { 3105019, 147016992}, { 3562844, 147482514}, { 3900940, 147829488}, { 3926192, 147851556}, { 5456634, 149216502}, { 5473415, 149229592}, { 5678115, 149389248},  | ||||
| 		{ 6416516, 149979537}, { 6693887, 150160404}, { 7011978, 150367823}, { 8034093, 151060650}, { 8245822, 151174920}, { 8663509, 151400371}, { 8734568, 151444233}, { 9700825, 151913516}, { 10314440, 151101573}, { 10876143, 150241580},  | ||||
| 		{ 10937084, 150142918}, { 10989872, 150057455}, { 11012110, 150016058}, { 11029139, 149984364}, { 11202330, 149640866}, { 11331097, 149385460}, { 11601540, 148893595}, { 11801542, 148453984}, { 11867898, 148312015}, { 12006182, 148016156},  | ||||
| 		{ 11936334, 147987685}, { 11846756, 147951181}, { 11775937, 147908929}, { 11448318, 147578728}, { 10005162, 146006655}, { 9941330, 145934468}, { 9420742, 145345782}, { 9364739, 145276533}, { 9005053, 144831776}, { 8354706, 143947082},  | ||||
| 		{ 7741954, 143034251}, { 7046776, 141998616}, { 6866979, 141726486}, { 6759755, 141551328}, { 6581042, 141228382}, { 6214827, 140566592}, { 6160332, 140464737}, { 6154984, 140452943}, { 6118667, 140365627}, { 5608006, 139066930},  | ||||
| 		{ 5576877, 138974353}, { 5449821, 138579522}, { 5423448, 138473840}, { 5269717, 137857830}, { 5183256, 137323221}, { 5051763, 136885377}, { 4900390, 136381329}, { 4788716, 135926420}, { 4778542, 135832434}, { 4751278, 135580592},  | ||||
| 		{ 4759133, 135551850}, { 4772567, 135502722}, { 4750760, 135428400}, { 4689711, 135220325}, { 4720284, 134950229}, { 4772938, 134876983}, { 4872059, 134739100}, { 4907041, 134734799}, { 4988166, 134747911}, { 5187996, 134827143},  | ||||
| 		{ 5324282, 134881173}, { 5823633, 135095262}, { 6457261, 135576778}, { 6468046, 135585394}, { 6645027, 135726930}, { 7665807, 136625984}, { 8014871, 136908364}, { 8760642, 137511681}, { 9070115, 137764153}, { 9505207, 138067027},  | ||||
| 		{ 9692018, 138199840}, { 10866067, 139034528}, { 10974854, 139102654}, { 11199174, 139243162}, { 11980766, 139757269}, { 12820102, 140204762}, { 13013724, 140301821}, { 13307713, 140449197}, { 13339465, 140204984}, { 13387908, 139832384},  | ||||
| 		{ 13476326, 139229254}, { 13545245, 138464294}, { 13637934, 137435521}, { 13704650, 136750183}, { 13756310, 135946981}, { 13854009, 134427968}, { 13931781, 133352665}, { 13880515, 133326641}, { 13806176, 133288914}, { 13608773, 133095964},  | ||||
| 		{ 13229938, 132676064}, { 12917088, 132329299}, { 12475540, 131854762}, { 12234438, 131582242}, { 11645945, 130917061}, { 11435343, 130656410}, { 11256705, 130435328}, { 11087956, 130227341}, { 10943531, 130049329}, { 10660547, 129658000},  | ||||
| 		{ 9884836, 128504691}, { 9363495, 127729593}, { 9183437, 127445707}, { 8613352, 126392173}, { 8569664, 126295529}, { 8233135, 125484892}, { 8100143, 125150567}, { 8091324, 125125230}, { 8068370, 125055541}, { 8047369, 124966573},  | ||||
| 		{ 7827878, 124036734}, { 7815999, 123941440}, { 7743138, 123689990}, { 7467916, 122740178}, { 7381012, 122383130}, { 7365871, 122250909}, { 7330956, 121946008}, { 7347071, 121910652}, { 7366239, 121868607}, { 7337555, 121775565},  | ||||
| 		{ 7275180, 121573218}, { 7357784, 121255913}, { 7363162, 121248563}, { 7433561, 121152362}, { 7492882, 121172887}, { 8152120, 121400901}, { 8296078, 121458859}, { 8337642, 121483827}, { 8428744, 121552386}, { 8461373, 121578560},  | ||||
| 		{ 9113408, 122101612}, { 9968838, 122858025}, { 10418874, 123221408}, { 11203964, 123855334}, { 11236475, 123882487}, { 11264272, 123901717}, { 12869603, 125041315}, { 13619004, 125547677}, { 13833945, 125671552}, { 14049136, 125795572},  | ||||
| 		{ 14042979, 124631730}, { 14031124, 123791039}, { 14029618, 123425913}, { 14024871, 122275773}, { 14024680, 122240909}, { 14024300, 122172017}, { 14024368, 122132419}, { 14024494, 122058437}, { 14025750, 122003675}, { 14028093, 121901540},  | ||||
| 		{ 14053706, 121051621}, { 14084937, 120015176}, { 13976495, 119893307}, { 12808105, 118596333}, { 12632795, 118395530}, { 12332420, 118051483}, { 12010936, 117651678}, { 11662489, 117218341}, { 11286185, 116695820}, { 10542401, 115590915},  | ||||
| 		{ 10484664, 115505145}, { 10085127, 114875400}, { 9677465, 114107097}, { 9676038, 114103997}, { 9587011, 113910478}, { 9572058, 113874387}, { 9221672, 113028545}, { 9132465, 112762183}, { 8929936, 112011523}, { 8896027, 111773355},  | ||||
| 		{ 8763540, 111338847}, { 8591711, 110775312}, { 8585822, 110750616}, { 8583286, 110726469}, { 8532504, 110242770}, { 8561517, 110201837}, { 8589689, 110162093}, { 8539283, 109999835}, { 8459773, 109743891}, { 8476274, 109635698},  | ||||
| 		{ 8539247, 109532026}, { 8559299, 109499015}, { 8639538, 109407427}, { 8837219, 109481673}, { 9374636, 109713713}, { 9614985, 109884378}, { 9895885, 110108176}, { 10150796, 110311272}, { 10647433, 110745796}, { 11163900, 111149653},  | ||||
| 		{ 11435641, 111378216}, { 11952173, 111812662}, { 12063358, 111892355}, { 12195941, 111987389}, { 13754894, 113077948}, { 13965930, 113207021}, { 14143358, 113315534}, { 14095680, 112195851}, { 14075275, 111736247}, { 14031684, 110754424},  | ||||
| 		{ 13949266, 109698295}, { 13931155, 109374956}, { 13907232, 108947887}, { 13903305, 108820557}, { 13899752, 108705317}, { 13898286, 108692370}, { 13896892, 108680054}, { 13882077, 108455610}, { 13866991, 108227067}, { 13852378, 107897586},  | ||||
| 		{ 13627196, 107630194}, { 13249326, 107173733}, { 13128837, 107021896}, { 12504668, 106235327}, { 12449045, 106156712}, { 12301165, 105947708}, { 12240927, 105864439}, { 12071292, 105629917}, { 11741182, 105140360}, { 11102902, 104050785},  | ||||
| 		{ 11009874, 103891983}, { 10724262, 103375048}, { 10370561, 102607103}, { 10302463, 102446702}, { 9995869, 101563023}, { 9933827, 101340326}, { 9788639, 100674614}, { 9761576, 100425516}, { 9620310, 99895785}, { 9572074, 99714909},  | ||||
| 		{ 9473316, 99261511}, { 9457110, 98860065}, { 9475422, 98813097}, { 9491516, 98771818}, { 9454445, 98628574}, { 9395112, 98399301}, { 9430018, 98201406}, { 9448015, 98172416}, { 9519385, 98057456}, { 9858391, 98155219}, { 10045563, 98209192},  | ||||
| 		{ 10217386, 98274096}, { 10328458, 98365757}, { 11168922, 99136589}, { 11517095, 99428522}, { 11782963, 99664460}, { 12152171, 99992110}, { 12543518, 100270019}, { 12914813, 100533689}, { 13199749, 100744460}, { 13324020, 100835567},  | ||||
| 		{ 13585579, 101027330}, { 13575682, 100826649}, { 13569447, 100700201}, { 13562345, 100567361}, { 13559065, 100506021}, { 13429751, 98521010}, { 13371150, 97621467}, { 13343156, 97180710}, { 13333987, 97039073}, { 13207473, 95084673},  | ||||
| 		{ 13138184, 95005008}, { 13017680, 94866468}, { 12083312, 93848129}, { 12022705, 93771797}, { 11862461, 93569995}, { 11784430, 93470508}, { 11589381, 93221813}, { 11309567, 92840780}, { 10844778, 92098029}, { 10775191, 91976786},  | ||||
| 		{ 10496881, 91491862}, { 10185086, 90849349}, { 10144137, 90764963}, { 10074833, 90600171}, { 9828579, 89857830}, { 9703614, 89075796}, { 9674971, 88969502}, { 9495272, 88102892}, { 9475468, 87916753}, { 9440640, 87589408}, { 9465676, 87528619},  | ||||
| 		{ 9487914, 87474617}, { 9465041, 87357340}, { 9428525, 87170118}, { 9490390, 86904119}, { 9512256, 86883153}, { 9574632, 86823334}, { 9727402, 86841642}, { 10166330, 86894255}, { 10190151, 86899193}, { 10198409, 86903284}, { 10249971, 86942095},  | ||||
| 		{ 10299758, 86980568}, { 11788945, 88131376}, { 11901024, 88196968}, { 12050012, 88284161}, { 12770268, 88697024}, { 12893258, 88767518}, { 12865978, 88340499}, { 12755514, 86383203}, { 12590001, 84209400}, { 12584956, 84143148},  | ||||
| 		{ 12549052, 83666692}, { 11929877, 83107725}, { 11390770, 82556851}, { 11083660, 82243035}, { 10537284, 81546957}, { 10424674, 81403496}, { 10079867, 80926984}, { 9689286, 80270083}, { 9687616, 80267071}, { 9615613, 80136762},  | ||||
| 		{ 9601056, 80104215}, { 9309849, 79453353}, { 9259598, 79312241}, { 9118888, 78788995}, { 9088297, 78585733}, { 8994447, 78301695}, { 8881493, 77959840}, { 8828452, 77771748}, { 8812025, 77688113}, { 8733303, 77287329}, { 8744431, 77274990},  | ||||
| 		{ 8786674, 77228142}, { 8770800, 77179143}, { 8746495, 77100409}, { 8709758, 76981397}, { 8765710, 76725562}, { 8780763, 76703647}, { 8821796, 76643902}, { 9050198, 76667553}, { 9430163, 76706910}, { 9505621, 76721216}, { 9535183, 76740071},  | ||||
| 		{ 9561982, 76753134}, { 11021709, 77681521}, { 11271938, 77809531}, { 11740477, 78049225}, { 11713323, 77940287}, { 11500961, 77088300}, { 11491445, 77055518}, { 11468672, 76977103}, { 11007894, 75454998}, { 10625858, 74342820},  | ||||
| 		{ 10581531, 74223613}, { 10547931, 74133250}, { 9872558, 72487409}, { 9785055, 72279833}, { 9735127, 72161397}, { 9661531, 72007947}, { 9614591, 71910070}, { 9234200, 71112437}, { 9147114, 70727104}, { 9142261, 70702843}, { 9138267, 70682891},  | ||||
| 		{ 9286224, 70443224}, { 9461343, 70247284}, { 9556416, 70162672}, { 9625168, 70101485}, { 9435737, 69811425}, { 9287394, 69584273}, { 8757085, 68530695}, { 8673850, 68365333}, { 8445942, 67877601}, { 8187617, 67187177}, { 8139627, 67039434},  | ||||
| 		{ 7937861, 66234567}, { 7892565, 65909655}, { 7845288, 65439718}, { 7844011, 65310767}, { 7823103, 65136343}, { 7778117, 64761067}, { 7716333, 64313964}, { 7705694, 64124356}, { 7687717, 63803945}, { 7701643, 63790152}, { 7718011, 63773943},  | ||||
| 		{ 7758186, 63752036}, { 7729172, 63586572}, { 7697769, 63407488}, { 7779619, 63146399}, { 7790119, 63132497}, { 7857734, 63042993}, { 7899799, 63053366}, { 8115923, 63106666}, { 8464711, 63240292}, { 8677072, 63398904}, { 8767176, 63477143},  | ||||
| 		{ 8977927, 63660143}, { 9421383, 64100703}, { 9785048, 64413088}, { 9975436, 64589567}, { 10286420, 64877827}, { 11014721, 65410888}, { 11115862, 65482249}, { 11327524, 65631599}, { 11395991, 65675856}, { 11535890, 65766274}, { 12026448, 66109919},  | ||||
| 		{ 12502690, 66343355}, { 12786634, 66472769}, { 13164960, 66645193}, { 13207596, 66564001}, { 13256756, 66470394}, { 13640736, 65570500}, { 13683003, 65454507}, { 13718537, 65356988}, { 13735231, 65270567}, { 13747424, 65207437},  | ||||
| 		{ 13863686, 64629409}, { 13875328, 64496043}, { 13887975, 64351165}, { 13957488, 63607260}, { 13950883, 63386188}, { 13943973, 63154947}, { 13895952, 62476120}, { 13876483, 62262044}, { 13859838, 62079009}, { 13859584, 62074662},  | ||||
| 		{ 13859582, 62065658}, { 13859483, 61971042}, { 13862761, 55222623}, { 13815791, 55212684}, { 13617475, 55174296}, { 13379849, 55128299}, { 13200660, 55067043}, { 13117648, 55038667}, { 12798922, 54907256}, { 12743350, 54730557},  | ||||
| 		{ 12719703, 54655364}, { 12656225, 54324243}, { 12632418, 53676660}, { 12625539, 53489551}, { 12652785, 53052852}, { 12782795, 52820186}, { 12846930, 52705411}, { 13041220, 52491209}, { 13143647, 52409064}, { 13187810, 52373646},  | ||||
| 		{ 13354789, 52319639}, { 13381838, 52313108}, { 13407786, 52306845}, { 13609096, 52308186}, { 13798532, 52309451}, { 14794521, 52294618}, { 15549961, 52336594}, { 16050147, 52311338}, { 16209513, 52303295}, { 16312325, 52297439},  | ||||
| 		{ 16369590, 51869307}, { 16340473, 51576398}, { 16331091, 51482008}, { 16316170, 51377054}, { 16241360, 51186578}, { 16186688, 51047373}, { 16076915, 50725256}, { 16093629, 50461603}, { 16098435, 50385771}, { 16109774, 50333994},  | ||||
| 		{ 16208639, 50141731}, { 16271132, 50020206}, { 16284775, 49997056}, { 16295310, 49985147}, { 16360397, 49947770}, { 16432796, 49916484}, { 16999910, 49671395}, { 17079341, 49631019}, { 17221011, 49559013}, { 17356128, 49546264},  | ||||
| 		{ 17369996, 49528116}, { 17426993, 49502498}, { 17530282, 49456075}, { 17342293, 49148088}, { 17284381, 49008875}, { 17254026, 48935905}, { 17357436, 48625105}, { 17422365, 48429965}, { 17423796, 48426977}, { 17601162, 48056939},  | ||||
| 		{ 17599241, 47980228}, { 17595410, 47827198}, { 17579402, 47751708}, { 17538195, 47557388}, { 17400788, 46598168}, { 17023471, 46464319}, { 16973301, 46446494}, { 16812540, 46389386}, { 16673736, 46329440}, { 16319654, 46176525},  | ||||
| 		{ 15950663, 46003440}, { 15838028, 45939836}, { 15697899, 45836427}, { 15289766, 45502367}, { 15260072, 45476441}, { 14999104, 45248614}, { 14962927, 45210840}, { 14722491, 44959778}, { 14678301, 44921783}, { 14404868, 44686698},  | ||||
| 		{ 14020130, 44298671}, { 13905758, 44155324}, { 13566066, 43648328}, { 13163266, 43047144}, { 13102631, 42937239}, { 13070977, 42862998}, { 12945977, 42560557}, { 12902489, 42448510}, { 12696099, 41916758}, { 12684650, 41857975},  | ||||
| 		{ 12656516, 41713516}, { 12557005, 40938961}, { 12554067, 40837978}, { 12550435, 40713161}, { 12562692, 40535359}, { 12575839, 40344643}, { 12609216, 40034504}, { 12660395, 39915667}, { 12708691, 39803526}, { 12798899, 39599814},  | ||||
| 		{ 12938906, 39372986}, { 12995589, 39281154}, { 13232289, 39007147}, { 13498241, 38725717}, { 13591444, 38550048}, { 13628611, 38480001}, { 13631794, 38446522}, { 13586786, 38388985}, { 13507530, 38236091}, { 13096257, 38028857},  | ||||
| 		{ 12821362, 37838492}, { 12551686, 37651741}, { 12445887, 37503612}, { 12369283, 37396362}, { 12264258, 37242462}, { 12195026, 37044172}, { 12148552, 36863589}, { 12101329, 36680088}, { 12142095, 35348959}, { 12144651, 35291418},  | ||||
| 		{ 12162788, 34883134}, { 12163706, 34850506}, { 12168637, 34675334}, { 12163420, 34644423}, { 12134883, 34475307}, { 12106311, 33932082}, { 12095021, 33476333}, { 12094122, 33057779}, { 12092211, 32168031}, { 12100800, 31962352},  | ||||
| 		{ 12107580, 31800023}, { 12116077, 31640101}, { 12122543, 31518406}, { 12193613, 31111725}, { 12255946, 30755035}, { 12655685, 28642673}, { 12654000, 28322388}, { 12689137, 28120452}, { 12708722, 28007885}, { 12692342, 27740702},  | ||||
| 		{ 12770201, 27316837}, { 12810004, 27100162}, { 12822406, 26990057}, { 12840969, 26876333}, { 12930142, 26507364}, { 13006294, 26192274}, { 13140275, 25812749}, { 13171909, 25737294}, { 13213594, 25637871}, { 13513395, 24982223},  | ||||
| 		{ 13564918, 24904642}, { 13614340, 24830229}, { 13673478, 24765245}, { 13723561, 24710211}, { 13790283, 24595233}, { 13857122, 24480057}, { 14153860, 24116007}, { 14231993, 24020147}, { 14248273, 23981550}, { 14451243, 23786195},  | ||||
| 		{ 14602942, 23651634}, { 14684407, 23579375}, { 15221344, 23339532}, { 15255414, 23324310}, { 15480802, 23178412}, { 15646843, 23091400}, { 16018697, 22744059}, { 16456749, 22567685}, { 16708674, 22466255}, { 16837697, 22410158},  | ||||
| 		{ 17154392, 22190832}, { 17069931, 22106918}, { 17007737, 21985244}, { 16978925, 21928875}, { 17036320, 21826992}, { 17212750, 21670157}, { 17298093, 21594293}, { 17451145, 21457485}, { 17530883, 21256458}, { 17541075, 21230767},  | ||||
| 		{ 17549886, 21207629}, { 17244063, 20372250}, { 17209346, 20248411}, { 17092010, 20089995}, { 17023648, 19955801}, { 16984483, 19912896}, { 16834254, 19784836}, { 16625524, 19606905}, { 16620983, 19603024}, { 16616582, 19597241},  | ||||
| 		{ 16614255, 19589014}, { 16578856, 19463898}, { 16588025, 19439937}, { 16595650, 19420015}, { 16602627, 19365704}, { 16608518, 19319848}, { 16694764, 19210381}, { 16784442, 19096556}, { 16461235, 17851161}, { 16421291, 17669728},  | ||||
| 		{ 16359955, 17616552}, { 16304854, 17528237}, { 16266671, 17467038}, { 16001330, 17343372}, { 15927109, 17308781}, { 15828509, 17248124}, { 15766385, 17190011}, { 15678175, 17097940}, { 15629868, 17047518}, { 15678592, 16534350},  | ||||
| 		{ 15695434, 16356965}, { 15704034, 16303332}, { 15705308, 16269732}, { 15725443, 15743784}, { 15808595, 15332260}, { 15821377, 15312568}, { 15838901, 15285580}, { 15993537, 15201723}, { 16119571, 15175593}, { 16189683, 15163592},  | ||||
| 		{ 16237347, 15155438}, { 16508759, 15159065}, { 16375757, 14977910}, { 16282021, 14850635}, { 15510709, 13877187}, { 15342882, 13710959}, { 15237532, 13606608}, { 14831965, 13239743}, { 14581428, 13013122}, { 14293147, 12740902},  | ||||
| 		{ 14190984, 12660109}, { 13669460, 12247703}, { 12564414, 11331695}, { 12487187, 11271158}, { 12046686, 10925925}, { 11871179, 10835479}, { 11582487, 10686699}, { 11523291, 10654160}, { 11396348, 10324251}, { 11575096, 9791088},  | ||||
| 		{ 11656410, 9657529}, { 11694903, 9594301}, { 12154341, 8957487}, { 12327404, 8717611}, { 12920992, 7861977}, { 13163209, 7541046}, { 13299428, 7360558}, { 13534727, 7094968}, { 13607608, 7012705}, { 14344532, 6120949}, { 15087045, 5393680},  | ||||
| 		{ 15307430, 5177820}, { 15930737, 4553097}, { 16730116, 3841678}, { 17107544, 3505773}, { 17287251, 3346015}, { 17407773, 3251557}, { 17762201, 2970942}, { 18238970, 2593464}, { 18584923, 2367852}, { 18697829, 2294226}, { 18997703, 2084694},  | ||||
| 		{ 19253265, 1922140}, { 19413044, 1820512}, { 20082389, 1425058}, { 21018405, 914454}, { 21306702, 757182}, { 21909855, 426548}, { 22232009, 276063}, { 22432844, 180461}, { 22572399, 114027}, { 22900298, 67093} | ||||
| 	}; | ||||
| 	out.holes.emplace_back(Slic3r::Points( { | ||||
| 		{ 28812659, 51882256}, { 28813904, 51895244}, { 28807002, 51890550}, { 28850702, 52059657}, { 28856299, 52123368}, { 29045593, 52135332}, { 29004080, 52024610}, { 28932623, 51976002}, { 29332407, 51880142}, { 29334099, 51804647},  | ||||
| 		{ 29252306, 51781113}, { 29155613, 51753292}, { 28890648, 51728889}, { 28797131, 51720277} | ||||
| 	} )); | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| SCENARIO("Elephant foot compensation", "[ElephantFoot]") { | ||||
| 
 | ||||
| 	GIVEN("Contour with hole") { | ||||
| 		ExPolygon expoly =  contour_with_hole(); | ||||
| 		WHEN("Compensated") { | ||||
| 			// Elephant foot compensation shall not pinch off bits from this contour.
 | ||||
| 			ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.2f); | ||||
| #ifdef TESTS_EXPORT_SVGS | ||||
| 			SVG::export_expolygons(debug_out_path("elephant_foot_compensation_with_hole.svg").c_str(), | ||||
| 				{ { { expoly },             { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, | ||||
| 				  { { expoly_compensated }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } } }); | ||||
| #endif /* TESTS_EXPORT_SVGS */ | ||||
|             THEN("area of the compensated polygon is smaller") { | ||||
|                 REQUIRE(expoly_compensated.area() < expoly.area()); | ||||
|             } | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	GIVEN("Tiny contour") { | ||||
| 		ExPolygon expoly({ { 133382606, 94912473 }, { 134232493, 95001115 }, { 133783926, 95159440 }, { 133441897, 95180666 }, { 133408242, 95191984 }, { 133339012, 95166830 }, { 132991642, 95011087 }, { 133206549, 94908304 } }); | ||||
| 		WHEN("Compensated") { | ||||
| 			ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.2f); | ||||
| #ifdef TESTS_EXPORT_SVGS | ||||
| 			SVG::export_expolygons(debug_out_path("elephant_foot_compensation_tiny.svg").c_str(), | ||||
| 				{ { { expoly },             { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, | ||||
| 				  { { expoly_compensated }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } } }); | ||||
| #endif /* TESTS_EXPORT_SVGS */ | ||||
| 			THEN("Tiny contour is not compensated") { | ||||
| 				REQUIRE(expoly_compensated == expoly); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	GIVEN("Large box") { | ||||
| 		ExPolygon expoly( { {50000000, 50000000 }, { 0, 50000000 }, { 0, 0 }, { 50000000, 0 } } ); | ||||
|         WHEN("Compensated") { | ||||
|  |  | |||
|  | @ -252,15 +252,39 @@ SCENARIO("Circle Fit, TaubinFit with Newton's method", "[Geometry]") { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("Chained path working correctly", "[Geometry]"){ | ||||
|     // if chained_path() works correctly, these points should be joined with no diagonal paths
 | ||||
|     // (thus 26 units long)
 | ||||
|     std::vector<Point> points = {Point(26,26),Point(52,26),Point(0,26),Point(26,52),Point(26,0),Point(0,52),Point(52,52),Point(52,0)}; | ||||
|     std::vector<Points::size_type> indices = chain_points(points); | ||||
|     for (Points::size_type i = 0; i + 1 < indices.size(); ++ i) { | ||||
|         double dist = (points.at(indices.at(i)).cast<double>() - points.at(indices.at(i+1)).cast<double>()).norm(); | ||||
|         REQUIRE(std::abs(dist-26) <= EPSILON); | ||||
|     } | ||||
| SCENARIO("Path chaining", "[Geometry]") { | ||||
| 	GIVEN("A path") { | ||||
| 		std::vector<Point> points = { Point(26,26),Point(52,26),Point(0,26),Point(26,52),Point(26,0),Point(0,52),Point(52,52),Point(52,0) }; | ||||
| 		THEN("Chained with no diagonals (thus 26 units long)") { | ||||
| 			std::vector<Points::size_type> indices = chain_points(points); | ||||
| 			for (Points::size_type i = 0; i + 1 < indices.size(); ++ i) { | ||||
| 				double dist = (points.at(indices.at(i)).cast<double>() - points.at(indices.at(i+1)).cast<double>()).norm(); | ||||
| 				REQUIRE(std::abs(dist-26) <= EPSILON); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	GIVEN("Loop pieces") { | ||||
| 		Point a { 2185796, 19058485 }; | ||||
| 		Point b { 3957902, 18149382 }; | ||||
| 		Point c { 2912841, 18790564 }; | ||||
| 		Point d { 2831848, 18832390 }; | ||||
| 		Point e { 3179601, 18627769 }; | ||||
| 		Point f { 3137952, 18653370 }; | ||||
| 		Polylines polylines = { { a, b }, | ||||
| 								{ c, d }, | ||||
| 								{ e, f }, | ||||
| 								{ d, a }, | ||||
| 								{ f, c }, | ||||
| 								{ b, e } }; | ||||
| 		Polylines chained = chain_polylines(polylines, &a); | ||||
| 		THEN("Connected without a gap") { | ||||
| 			for (size_t i = 0; i < chained.size(); ++i) { | ||||
| 				const Polyline &pl1 = (i == 0) ? chained.back() : chained[i - 1]; | ||||
| 				const Polyline &pl2 = chained[i]; | ||||
| 				REQUIRE(pl1.points.back() == pl2.points.front()); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| SCENARIO("Line distances", "[Geometry]"){ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Enrico Turri
						Enrico Turri