Comparar commits
457 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| f1d59af2a9 | |||
| 7d354ca587 | |||
| 7560250348 | |||
| 9b7a985b54 | |||
| db0c47e314 | |||
| 4aed36d7d5 | |||
| 44b0522447 | |||
| 464fb29bca | |||
| fbf989282b | |||
| db99b46506 | |||
| c7ffd5b23d | |||
| b8d5474811 | |||
| 4c60339695 | |||
| e1a594ad3e | |||
| 94d9a948ae | |||
| 176bf2c887 | |||
| 3cf208f8ed | |||
| 08eec2ad1f | |||
| 41e012be5f | |||
| 8102f7a246 | |||
| d527358a82 | |||
| 6115bdec2a | |||
| 9f29cbd3fb | |||
| 5c3727d532 | |||
| 73152d84c7 | |||
| 2b3f22872e | |||
| c7ce43eb6c | |||
| d9e2b39baf | |||
| fa03c3823f | |||
| 34e86195e9 | |||
| bad780c54b | |||
| 3ddbc1d53b | |||
| 51757353b4 | |||
| d931baa788 | |||
| 2cf0d40775 | |||
| d20df39d5f | |||
| 57bc411bc6 | |||
| 0c30d27892 | |||
| 0c9562b273 | |||
| 321a9c11e0 | |||
| 389950ed90 | |||
| 61e5187267 | |||
| 3859d58571 | |||
| 345c599062 | |||
| 73625b949f | |||
| bf1cf334e6 | |||
| 13ec686608 | |||
| 742eedda06 | |||
| bc6a139d1f | |||
| 9770e91961 | |||
| 9e3dfb5b12 | |||
| 8b244c1738 | |||
| c05c3ddaed | |||
| 804c288353 | |||
| 22c1ff4efe | |||
| 811be991c1 | |||
| 684da831b2 | |||
| a9ff4a3826 | |||
| 6d2ecc7811 | |||
| e77ae7362b | |||
| 571d592136 | |||
| 4d1ebb33e6 | |||
| 995f9786a4 | |||
| 3467593aa2 | |||
| ef4b8abaf5 | |||
| f460eeef57 | |||
| 488f82ccbc | |||
| db3e9ac5e6 | |||
| 8529924bc4 | |||
| 03ff861904 | |||
| b3752a43b4 | |||
| 7e47664a51 | |||
| 1b6b5cb843 | |||
| 0d98c6eaef | |||
| 518a528596 | |||
| c9ea663a1b | |||
| 0a7355d25c | |||
| fc6cad48af | |||
| d4c7abf7ef | |||
| c04274401a | |||
| ee8dd68046 | |||
| afcacd00a7 | |||
| a5b31de404 | |||
| c8f1922966 | |||
| ac24325715 | |||
| 4807e13de6 | |||
| b389b24d9b | |||
| 121d13b57e | |||
| b1dc67be71 | |||
| 2584c7b38c | |||
| ac8b0e149a | |||
| 03141f505d | |||
| e18659669e | |||
| 903f3b11d1 | |||
| 51f27fd0ca | |||
| b7d1e53992 | |||
| 461a90a319 | |||
| b19c6751c5 | |||
| e2479e12b0 | |||
| c67f691237 | |||
| 9c8e32f37b | |||
| 124b116b0c | |||
| 0b8fd8209c | |||
| 1534ff0361 | |||
| b6268d8dc9 | |||
| 3413f3abaa | |||
| cb5c69d064 | |||
| afa7c5a0c0 | |||
| 7cbfd42b8f | |||
| 515bf4592e | |||
| 667644eee4 | |||
| 17e00733a4 | |||
| 5142f8c1ba | |||
| 432abf11d5 | |||
| 77361417a4 | |||
| 83da6b2072 | |||
| e567f8265a | |||
| c20b07fe0d | |||
| 10815b4f93 | |||
| a119be9021 | |||
| 002692fe18 | |||
| 99e9054f54 | |||
| eedd4adee3 | |||
| e110b864ce | |||
| 1109f97ae6 | |||
| 469ab32b5c | |||
| e1390582ed | |||
| 6b1b11b47b | |||
| 3526edd04a | |||
| a101be9d2f | |||
| 52aedafc13 | |||
| afd1e2659e | |||
| 93896a6960 | |||
| 954a396d96 | |||
| 610880737f | |||
| 36ed9904b5 | |||
| 996fc2cbc1 | |||
| 7af12e5526 | |||
| 0323179b4a | |||
| 91956ad0e6 | |||
| ff9dbf9a51 | |||
| bbff888e23 | |||
| 4460b9a1d0 | |||
| dd161ef770 | |||
| 30050349fa | |||
| bafcb804f4 | |||
| a7320a2d14 | |||
| a7e72ea917 | |||
| 61c9cb5a07 | |||
| d029d7294f | |||
| 4c2aaed215 | |||
| 68602df6ee | |||
| 017fb03391 | |||
| 92f5311aaf | |||
| 00abb3ab4c | |||
| 4f9f55ab06 | |||
| dfc91d811f | |||
| 2bfdb2d612 | |||
| 026f82540f | |||
| 835424121f | |||
| d87072f761 | |||
| f89a747836 | |||
| 767803d2bf | |||
| a808e9e8ed | |||
| 087efefc16 | |||
| 09afe2264a | |||
| 56d6d9196f | |||
| 6866925a05 | |||
| 5ede92fb1b | |||
| e9c9157ac8 | |||
| 8ad0416428 | |||
| 321ef643a4 | |||
| c43c876cbc | |||
| 3d596f1765 | |||
| ff747fa88f | |||
| 950441493c | |||
| 0c7cca6f17 | |||
| 41a1db1e55 | |||
| c058a2b12b | |||
| adc8341e49 | |||
| 0717594a15 | |||
| d6ed95ed81 | |||
| c72b05463a | |||
| eb1acd02a2 | |||
| 102b9f9c75 | |||
| abb0b9a763 | |||
| 63eda27a50 | |||
| 5b1eeecf76 | |||
| 41ede0727b | |||
| 7ffec2e617 | |||
| 57ebcf0163 | |||
| 71ae42a949 | |||
| 49a1ed7f3b | |||
| 190b5b789b | |||
| ad29876617 | |||
| 07481a548c | |||
| a6be90d3b4 | |||
| 3231d75d94 | |||
| 5e4080e0bb | |||
| 6432215ad9 | |||
| 3a60d0cb01 | |||
| 7d70768178 | |||
| 9c201e3396 | |||
| f00c091ed1 | |||
| 4574e0917e | |||
| 4ae1f5e6bf | |||
| ae02a91a5d | |||
| 0781a8f4ef | |||
| f1902d88bf | |||
| 4cc5c35564 | |||
| f124a7e528 | |||
| 1cd8e1c500 | |||
| 6dc89bbe7f | |||
| 16250c54c9 | |||
| c6e201e307 | |||
| c5be07212e | |||
| 7035e91ac6 | |||
| 4f972da7ae | |||
| 47718a1ba4 | |||
| 87db7232a1 | |||
| b9f9c12d6c | |||
| 046fa3ba8e | |||
| db039033ce | |||
| c0fa1b4a1a | |||
| 1d073600e4 | |||
| 8f6cb82421 | |||
| a455913165 | |||
| 9b0e4be570 | |||
| 1023102e3a | |||
| 0ac334c849 | |||
| 696e0132f4 | |||
| f9a0e39a6a | |||
| ff9e7f11ae | |||
| fb284397c2 | |||
| f5e2c5a070 | |||
| 85cfd422ba | |||
| c5334e355e | |||
| 086c38ac0b | |||
| 4cb64bab67 | |||
| 87eaa97f21 | |||
| 59fc004f09 | |||
| 5dbfab6ffb | |||
| 5494ce12a5 | |||
| 1ed1de147a | |||
| 1c0a616ec7 | |||
| 82a4bff953 | |||
| f7bbda7599 | |||
| 273f1f46f3 | |||
| e81abe405d | |||
| d0dd97ab04 | |||
| d0e6f5c07e | |||
| f4eb33e14e | |||
| 3d3ddc238f | |||
| 704edd25af | |||
| e33560beb6 | |||
| 71e486d007 | |||
| 4ca66b6916 | |||
| 7e6c4661b9 | |||
| f96eaddf6c | |||
| eb2ae0d699 | |||
| bcee6c7cc2 | |||
| 3a28f5d547 | |||
| 5c41425859 | |||
| 85959b0aa7 | |||
| 6bd1d9fe8c | |||
| 3e43df9e60 | |||
| 4b11b5946e | |||
| d558e24b43 | |||
| 5d6f45a71d | |||
| d827cc0f92 | |||
| c185c846e6 | |||
| e28cef3272 | |||
| 777f45ac16 | |||
| a9a3c8a4b7 | |||
| ad2cf3b7a6 | |||
| a57ec01cc4 | |||
| c787ffad02 | |||
| faa580ffe6 | |||
| 16679d68d0 | |||
| 9983ebac7b | |||
| ee8ef2bfa5 | |||
| 7646426f9d | |||
| 263eb370e9 | |||
| c0d22dcf7b | |||
| 01d0a3b8d6 | |||
| 1dd7313332 | |||
| 7a7d1ebd77 | |||
| 2976b1276c | |||
| b84f41d9b0 | |||
| f142df9176 | |||
| 94f90419dd | |||
| 90547bb69c | |||
| 3611d53222 | |||
| ab7ca73596 | |||
| c42568c059 | |||
| 43eb784b6c | |||
| 68f1eb1fd2 | |||
| 3dfb84b9a1 | |||
| 80896e3f4d | |||
| 6518e14e18 | |||
| 4c95e43048 | |||
| 4c66b7fd01 | |||
| 97ebfe8b12 | |||
| 0cb8b64f70 | |||
| bfc2d458ae | |||
| 365de0578c | |||
| d9c30de796 | |||
| e8cb1028e4 | |||
| bc41b415c2 | |||
| d4adbb97bf | |||
| c8488f6d37 | |||
| 7524df2868 | |||
| 28ac5dc2c9 | |||
| f5db2ee8b4 | |||
| 8e736f32aa | |||
| 9b4d93e236 | |||
| 2f4316e755 | |||
| 52e5b33e7e | |||
| 01c124774c | |||
| b4afc8bb4f | |||
| d4e2f774db | |||
| eb388c7d80 | |||
| ab5b7f432e | |||
| e0a09dbe7c | |||
| d83863ce4c | |||
| fe8def059e | |||
| b91abb4c22 | |||
| 4179e46a9d | |||
| 692e875256 | |||
| 9d513291ba | |||
| 5b0370864b | |||
| 4dcb8563f5 | |||
| 21b6be76fe | |||
| f06fa4af1a | |||
| 951eac4c08 | |||
| e4e2dc172e | |||
| 70b0800f2e | |||
| aaaf2a6c92 | |||
| bde136e7ed | |||
| 75859623d7 | |||
| 388a65ef11 | |||
| 1168c2b120 | |||
| dfd31844ba | |||
| f457f2590c | |||
| fb9bcccbbe | |||
| cfe216ae44 | |||
| a41a1e556a | |||
| 8081237298 | |||
| 1a7137a6c8 | |||
| 4c967ec6a3 | |||
| b455661a75 | |||
| 46b7d1ccab | |||
| fb95022776 | |||
| 62ce0b5513 | |||
| 12c25b2214 | |||
| 61915fc322 | |||
| aa1998dbd8 | |||
| 859a106ec2 | |||
| ff38534644 | |||
| 2eb81d216f | |||
| 2785659fe6 | |||
| 4bc86ad192 | |||
| a7b5e05505 | |||
| 0fd130cfd3 | |||
| ccae7d611e | |||
| 5dcce74efe | |||
| 988af0f6ed | |||
| 53714319b4 | |||
| fe44537924 | |||
| a285b22804 | |||
| e588fe100b | |||
| ad81cc8a12 | |||
| a3db17442c | |||
| e0cf567bf7 | |||
| 904ad24eec | |||
| f49803b8a9 | |||
| 2b422ce6c0 | |||
| b2cc46a4cb | |||
| 98d105d73c | |||
| ecd32700f5 | |||
| 44c77d2a31 | |||
| f78c3ed4da | |||
| 79b16e406a | |||
| 8219f32d96 | |||
| 05cfe62927 | |||
| 38e832c172 | |||
| 64ef6e5698 | |||
| 769dc252df | |||
| 7093231707 | |||
| ad93a8adde | |||
| 1bb8bde1de | |||
| d6822cdaf8 | |||
| 2817528c51 | |||
| a52f101c14 | |||
| 22b1d427ec | |||
| b89733320d | |||
| f662844d78 | |||
| 38a4e45771 | |||
| dc87dda346 | |||
| f249796f3d | |||
| 9602616a6b | |||
| 0823f8faf5 | |||
| 33d63e8359 | |||
| 3a8a5844d4 | |||
| 7b5bb1e367 | |||
| 017dc5be4c | |||
| 60c9e83da9 | |||
| 51927938b0 | |||
| d4b9f7c0bd | |||
| 0b78099587 | |||
| a61d5385ea | |||
| 24c836f87a | |||
| 8a2ca62305 | |||
| b0ae307006 | |||
| 29dd62345a | |||
| b3f555c78f | |||
| 0b2f64c6a7 | |||
| d501fe67d5 | |||
| 7180e77699 | |||
| 9c36fdf513 | |||
| bc8a167e7b | |||
| a2881b83f7 | |||
| bd90dd060f | |||
| 1070d367e1 | |||
| 5ac62a96a8 | |||
| 7010689448 | |||
| 869975be6b | |||
| da3bf8da32 | |||
| e861fba11b | |||
| 226c974b5f | |||
| b25c72a9e3 | |||
| 6caf2b2db9 | |||
| b6138cb951 | |||
| e9b0cfc939 | |||
| f6e44af43d | |||
| 487f35abd4 | |||
| 2a163528f9 | |||
| 02e6c87396 | |||
| b367af6ad1 | |||
| eef8b77405 | |||
| 97a4c00022 | |||
| 6516ae0da9 | |||
| 360e3d7e24 | |||
| c267492a48 | |||
| 52258e2d3a | |||
| 92c6edca86 | |||
| 9c26c820a9 | |||
| b7993441ff | |||
| 10ebc96323 | |||
| 13af50f34d | |||
| 0563c6eacc | |||
| 6f95a78f3c | |||
| ae79d232f3 | |||
| 11786ada66 | |||
| 648bc1f3f3 | |||
| 84be436eb9 | |||
| e04965187a |
+22
-13
@@ -1,25 +1,34 @@
|
||||
*.tar.bz2
|
||||
*.tar.gz
|
||||
*.7z
|
||||
*.pyc
|
||||
*.zip
|
||||
*.exe
|
||||
.idea
|
||||
.DS_Store
|
||||
_bin
|
||||
_obj
|
||||
*.depend
|
||||
*.o
|
||||
.*.swp
|
||||
*.gcode
|
||||
CuraEngine
|
||||
build/*
|
||||
*~
|
||||
NUL
|
||||
*.gcode
|
||||
|
||||
## Building result.
|
||||
build/*
|
||||
*.pyc
|
||||
*.exe
|
||||
*.o
|
||||
CuraEngine
|
||||
_bin
|
||||
_obj
|
||||
|
||||
## IDE project files.
|
||||
CuraEngine.layout
|
||||
CuraEngine.cbp
|
||||
*kdev*
|
||||
*.kate-swp
|
||||
nbproject/*
|
||||
.idea
|
||||
*.depend
|
||||
.*.swp
|
||||
|
||||
## Documentation.
|
||||
documentation/html/*
|
||||
documentation/latex/*
|
||||
|
||||
*kdev*
|
||||
*.kate-swp
|
||||
## Test results.
|
||||
tests/output.xml
|
||||
|
||||
+55
-7
@@ -2,7 +2,14 @@ project(CuraEngine)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
find_package(Arcus REQUIRED)
|
||||
option (ENABLE_ARCUS
|
||||
"Enable support for ARCUS" ON)
|
||||
|
||||
if (ENABLE_ARCUS)
|
||||
message(STATUS "Building with Arcus")
|
||||
find_package(Arcus REQUIRED)
|
||||
add_definitions(-DARCUS)
|
||||
endif ()
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
@@ -10,6 +17,18 @@ else()
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11")
|
||||
endif()
|
||||
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
|
||||
set(CURA_ENGINE_VERSION "master" CACHE STRING "Version name of Cura")
|
||||
|
||||
option(BUILD_TESTS OFF)
|
||||
|
||||
# Add a compiler flag to check the output for insane values if we are in debug mode.
|
||||
if(CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
message(STATUS "Building debug release of CuraEngine.")
|
||||
add_definitions(-DASSERT_INSANE_OUTPUT)
|
||||
endif()
|
||||
|
||||
# Add warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
|
||||
@@ -21,7 +40,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} libs)
|
||||
|
||||
add_library(clipper STATIC libs/clipper/clipper.cpp)
|
||||
|
||||
set(engine_SRCS
|
||||
set(engine_SRCS # Except main.cpp.
|
||||
src/bridge.cpp
|
||||
src/comb.cpp
|
||||
src/commandSocket.cpp
|
||||
@@ -33,7 +52,7 @@ set(engine_SRCS
|
||||
src/infill.cpp
|
||||
src/inset.cpp
|
||||
src/layerPart.cpp
|
||||
src/main.cpp
|
||||
src/LayerPlanBuffer.cpp
|
||||
src/MergeInfillLines.cpp
|
||||
src/mesh.cpp
|
||||
src/MeshGroup.cpp
|
||||
@@ -60,15 +79,44 @@ set(engine_SRCS
|
||||
src/utils/polygon.cpp
|
||||
)
|
||||
|
||||
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
|
||||
# List of tests. For each test there must be a file tests/${NAME}.cpp and a file tests/${NAME}.h.
|
||||
set(engine_TEST
|
||||
GCodePlannerTest
|
||||
LinearAlg2DTest
|
||||
)
|
||||
|
||||
add_executable(CuraEngine ${engine_SRCS} ${engine_PB_SRCS})
|
||||
target_link_libraries(CuraEngine clipper Arcus)
|
||||
# Generating ProtoBuf protocol
|
||||
if (ENABLE_ARCUS)
|
||||
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
|
||||
endif ()
|
||||
|
||||
# Compiling CuraEngine itself.
|
||||
add_library(_CuraEngine ${engine_SRCS} ${engine_PB_SRCS}) #First compile all of CuraEngine as library, allowing this to be re-used for tests.
|
||||
target_link_libraries(_CuraEngine clipper)
|
||||
if (ENABLE_ARCUS)
|
||||
target_link_libraries(_CuraEngine Arcus)
|
||||
endif ()
|
||||
|
||||
set_target_properties(_CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(CuraEngine pthread)
|
||||
target_link_libraries(_CuraEngine pthread)
|
||||
endif()
|
||||
add_executable(CuraEngine src/main.cpp) #Then compile main.cpp as separate executable, and link the library to it.
|
||||
target_link_libraries(CuraEngine _CuraEngine)
|
||||
|
||||
# Compiling the test environment.
|
||||
if (BUILD_TESTS)
|
||||
message(STATUS "Building tests...")
|
||||
enable_testing()
|
||||
foreach (test ${engine_TEST})
|
||||
add_executable(${test} tests/main.cpp tests/${test}.cpp)
|
||||
target_link_libraries(${test} _CuraEngine cppunit)
|
||||
add_test(${test} ${test})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Installing CuraEngine.
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
include(CPackConfig.cmake)
|
||||
|
||||
+6
-13
@@ -1,19 +1,12 @@
|
||||
set(CPACK_PACKAGE_VENDOR "Ultimaker")
|
||||
set(CPACK_PACKAGE_CONTACT "Arjen Hiemstra <a.hiemstra@ultimaker.com>")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Cura Engine")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR 15)
|
||||
set(CPACK_PACKAGE_VERSION_MINOR 05)
|
||||
set(CPACK_PACKAGE_VERSION_PATCH 90)
|
||||
set(CPACK_GENERATOR "DEB;RPM")
|
||||
|
||||
set(RPM_REQUIRES
|
||||
"arcus >= 15.05.90"
|
||||
"protobuf >= 3.0.0"
|
||||
"libstdc++6 >= 4.9.0"
|
||||
"libgcc1 >= 4.9.0"
|
||||
)
|
||||
string(REPLACE ";" "," RPM_REQUIRES "${RPM_REQUIRES}")
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES ${RPM_REQUIRES})
|
||||
set(CPACK_PACKAGE_VERSION "15.05.90")
|
||||
set(CPACK_GENERATOR "DEB")
|
||||
if(NOT DEFINED CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
|
||||
execute_process(COMMAND dpkg --print-architecture OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||
|
||||
set(DEB_DEPENDS
|
||||
"arcus (>= 15.05.90)"
|
||||
|
||||
@@ -62,6 +62,8 @@ message Polygon {
|
||||
SkirtType = 5;
|
||||
InfillType = 6;
|
||||
SupportInfillType = 7;
|
||||
MoveCombingType = 8;
|
||||
MoveRetractionType = 9;
|
||||
}
|
||||
Type type = 1;
|
||||
bytes points = 2;
|
||||
|
||||
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 20 KiB |
@@ -1,126 +0,0 @@
|
||||
Code Conventions
|
||||
=======
|
||||
Note that the code convention described here have not all yet been fully implemented.
|
||||
|
||||
Bracketing and indenting
|
||||
-----
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
if (condition) // brackets always on new lines
|
||||
{ // allways a bracket after an if, for, while, etc.
|
||||
// indent always with 4 spaces, never with tabs
|
||||
}
|
||||
else // else on new line
|
||||
{
|
||||
// more code
|
||||
}
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Naming conventions
|
||||
------
|
||||
* variables: lower_case_with_underscores
|
||||
* functions: loweCamelCase
|
||||
* classes: UpperCamelCase
|
||||
* macros: UPPER_CASE_WITH_UNDERSCORES
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
#define UPPER_CASE_MACRO 1
|
||||
|
||||
class UpperCamelCase
|
||||
{
|
||||
private:
|
||||
MemberVariableObject with_underscores;
|
||||
public:
|
||||
MemberVariableObject with_underscores;
|
||||
|
||||
public:
|
||||
UpperCamelCase();
|
||||
~UpperCamelCase();
|
||||
|
||||
// start with input variable(s) and end with output variable(s)
|
||||
void lowerCamelCaseFunctions(ParamObject& also_with_underscores)
|
||||
{
|
||||
LocalObject under_scores;
|
||||
}
|
||||
private:
|
||||
void putFunctionsAndVariablesInSeperatePublicPrivateBlocks();
|
||||
};
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Ordering
|
||||
----
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
class Example
|
||||
{
|
||||
// start with input variable(s) and end with output parameter(s)
|
||||
void function1(ParamObject& input_variable, int setting_parameter, ParamObject2& return_parameter)
|
||||
{
|
||||
function2();
|
||||
function3();
|
||||
}
|
||||
|
||||
// place functions called solely by one other function below it chronologically
|
||||
void function2();
|
||||
|
||||
void function3();
|
||||
};
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Documentation
|
||||
----
|
||||
We use [Doxygen](www.doxygen.org/) to generate documentation. Try to keep your documentation in doxygen style.
|
||||
|
||||
Here's a small example:
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
/ *!
|
||||
* Doxygen style comments!
|
||||
*
|
||||
* \param param1 explanation may refer to another \p param2
|
||||
* /
|
||||
void function(int param1, int param2)
|
||||
{
|
||||
// non-doxygen style comments on implementation details
|
||||
}
|
||||
|
||||
int member; //!< inline doxygen comment on the entry to the left
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Files
|
||||
--------
|
||||
For a file Foo.h (UpperCamelCase):
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
#ifndef FOO_H
|
||||
#define FOO_H
|
||||
// [content]
|
||||
#endif//FOO_H
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
Other
|
||||
----
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
#include <all>
|
||||
#include <includes>
|
||||
#include <on>
|
||||
#include <top>
|
||||
|
||||
#include <first_system_includes>
|
||||
|
||||
#include <then_library_includes>
|
||||
|
||||
#include "finally_local_includes"
|
||||
|
||||
enum class EnumExample
|
||||
{
|
||||
ELEM0 = 0,
|
||||
ELEM1 = 1
|
||||
};
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Illegal syntax
|
||||
----
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
void function()
|
||||
{
|
||||
if (condition)
|
||||
single_line_outside_code_block(); // always use braces!
|
||||
}; // unneccesary semicolon after function definition is not allowed
|
||||
~~~~~~~~~~~~~~~
|
||||
@@ -0,0 +1,203 @@
|
||||
This file is a conversion from the ENGINE settings of 15.04 to the ENGINE setting of 2.0
|
||||
|
||||
This is NOT a conversion on the frontend internal setting names (15.04 has the dictionary of doom)
|
||||
|
||||
|
||||
autoCenter ? ==> center_object OR machine_center_is_zero ??
|
||||
coolHeadLift ==> cool_lift_head
|
||||
downSkinCount ==> bottom_layers
|
||||
enableCombing ==> retraction_combing
|
||||
enableOozeShield ==> ooze_shield_enabled
|
||||
endCode ==> machine_end_gcode
|
||||
extruderOffset[MAX_EXTRUDERS] = (machine_nozzle_offset_x, machine_nozzle_offset_y)
|
||||
extrusionWidth ==> infill_line_width, skirt_line_width, support_line_width
|
||||
fanFullOnLayerNr ==> cool_fan_full_layer
|
||||
fanSpeedMax ==> cool_fan_speed_max
|
||||
fanSpeedMin ==> cool_fan_speed_min
|
||||
filamentDiameter ==> material_diameter
|
||||
filamentFlow ==> material_flow
|
||||
fixHorrible ==> meshfix_union_all AND/OR meshfix_union_all_remove_holes AND/OR meshfix_extensive_stitching AND/OR magic_mesh_surface_mode
|
||||
gcodeFlavor ==> machine_gcode_flavor
|
||||
infillOverlap ==> infill_overlap
|
||||
infillPattern ==> infill_pattern
|
||||
infillSpeed ==> speed_infill
|
||||
initialLayerSpeed ==> speed_layer_0
|
||||
initialLayerThickness ==> layer_height_0
|
||||
initialSpeedupLayers ==> speed_slowdown_layers
|
||||
inset0Speed ==> speed_wall_0
|
||||
insetCount ==> wall_line_count
|
||||
insetXSpeed ==> speed_wall_x
|
||||
layer0extrusionWidth [ Doesn't exist anymore ]
|
||||
layerThickness ==> layer_height
|
||||
matrix [ Doesn't exist anymore ]
|
||||
minimalExtrusionBeforeRetraction
|
||||
minimalFeedrate ==> cool_min_speed
|
||||
minimalLayerTime ==> cool_min_layer_time
|
||||
moveSpeed ==> speed_travel
|
||||
multiVolumeOverlap ==> multiple_mesh_overlap
|
||||
nozzleSize ==> machine_nozzle_size
|
||||
objectPosition ==> mesh_position_x, mesh_position_y, mesh_position_z
|
||||
objectSink [ Doesn't exist in CuraEngine anymore ]
|
||||
perimeterBeforeInfill = not(infill_before_walls)
|
||||
postSwitchExtruderCode ==> machine_extruder_start_code
|
||||
preSwitchExtruderCode ==> machine_extruder_end_code
|
||||
printSpeed ==> speed_prime_tower, speed_support_lines, speed_support_roof, skirt_speed
|
||||
raftAirGap ==> raft_airgap
|
||||
raftAirGapLayer0 ?!?!?
|
||||
raftBaseLinewidth ==> raft_base_line_width
|
||||
raftBaseSpeed ==> raft_interface_speed, raft_base_speed
|
||||
raftBaseThickness ==> raft_base_thickness
|
||||
raftFanSpeed ==> raft_base_fan_speed, raft_interface_fan_speed, raft_surface_fan_speed
|
||||
raftInterfaceLineSpacing==> raft_interface_line_spacing
|
||||
raftInterfaceLinewidth ==> raft_interface_line_width
|
||||
raftInterfaceThickness ==> raft_interface_thickness
|
||||
raftLineSpacing ==> raft_base_line_spacing
|
||||
raftMargin ==> raft_margin
|
||||
raftSurfaceLayers ==> raft_surface_layers
|
||||
raftSurfaceLineSpacing ==> raft_surface_line_spacing
|
||||
raftSurfaceLinewidth ==> raft_surface_line_width
|
||||
raftSurfaceSpeed ==> raft_surface_speed
|
||||
raftSurfaceThickness ==> raft_surface_thickness
|
||||
retractionAmount ==> retraction_amount (set retraction_enable = true)
|
||||
retractionAmountExtruderSwitch ==> switch_extruder_retraction_amount
|
||||
retractionAmountPrime ==> retraction_extra_prime_amount
|
||||
retractionMinimalDistance ==> retraction_extrusion_window ( set retraction_count_max = 1 )
|
||||
retractionSpeed ==> retraction_retract_speed (, retraction_prime_speed ?), switch_extruder_retraction_speed
|
||||
retractionZHop ==> retraction_hop
|
||||
simpleMode ??!
|
||||
skinSpeed ==> speed_topbottom
|
||||
skirtDistance ==> skirt_gap
|
||||
skirtLineCount ==> brim_line_count, skirt_line_count
|
||||
skirtMinLength ==> skirt_minimal_length
|
||||
sparseInfillLineDistance ==> infill_line_distance
|
||||
spiralizeMode ==> magic_spiralize
|
||||
startCode ==> machine_start_gcode
|
||||
supportAngle ==> support_angle, support_enable=true if support_angle>0
|
||||
supportEverywhere ==> support_type
|
||||
supportExtruder ==> support_extruder_nr, support_extruder_nr_layer_0
|
||||
supportLineDistance ==> support_line_distance
|
||||
supportType ==> support_pattern
|
||||
supportXYDistance ==> support_xy_distance
|
||||
supportZDistance ==> support_z_distance
|
||||
upSkinCount ==> top_layers
|
||||
wipeTowerSize ==> prime_tower_size
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NEW:
|
||||
adhesion_extruder_nr
|
||||
adhesion_type
|
||||
alternate_extra_perimeter
|
||||
coasting_enable
|
||||
coasting_min_volume_move
|
||||
coasting_min_volume_retract
|
||||
coasting_speed_move
|
||||
coasting_speed_retract
|
||||
coasting_volume_move
|
||||
coasting_volume_retract
|
||||
cool_min_layer_time_fan_speed_max
|
||||
draft_shield_dist
|
||||
draft_shield_height
|
||||
extruder_nr
|
||||
fill_perimeter_gaps
|
||||
infill_sparse_thickness
|
||||
infill_wipe_dist
|
||||
machine_depth
|
||||
machine_extruder_count
|
||||
machine_extruder_end_pos_abs
|
||||
machine_extruder_end_pos_x
|
||||
machine_extruder_end_pos_y
|
||||
machine_extruder_start_pos_abs
|
||||
machine_extruder_start_pos_x
|
||||
machine_extruder_start_pos_y
|
||||
machine_heated_bed
|
||||
machine_nozzle_cool_down_speed
|
||||
machine_nozzle_expansion_angle
|
||||
machine_nozzle_head_distance
|
||||
machine_nozzle_heat_up_speed
|
||||
machine_nozzle_tip_outer_diameter
|
||||
machine_print_temp_wait
|
||||
machine_use_extruder_offset_to_offset_coords
|
||||
machine_width
|
||||
magic_fuzzy_skin_enabled
|
||||
magic_fuzzy_skin_point_dist
|
||||
magic_fuzzy_skin_thickness
|
||||
material_bed_temperature
|
||||
material_bed_temp_prepend
|
||||
material_bed_temp_wait
|
||||
material_extrusion_cool_down_speed
|
||||
material_flow_dependent_temperature
|
||||
material_flow_temp_graph
|
||||
material_print_temperature
|
||||
material_print_temp_prepend
|
||||
material_print_temp_wait
|
||||
material_standby_temperature
|
||||
meshfix_keep_open_polygons
|
||||
ooze_shield_angle
|
||||
ooze_shield_dist
|
||||
prime_tower_dir_outward
|
||||
prime_tower_distance
|
||||
prime_tower_flow
|
||||
prime_tower_line_width
|
||||
prime_tower_position_x
|
||||
prime_tower_position_y
|
||||
prime_tower_wipe_enabled
|
||||
remove_overlapping_walls_0_enabled
|
||||
remove_overlapping_walls_x_enabled
|
||||
retraction_min_travel
|
||||
skin_alternate_rotation
|
||||
skin_line_width
|
||||
skin_no_small_gaps_heuristic
|
||||
skin_outline_count
|
||||
support_area_smoothing
|
||||
support_bottom_distance
|
||||
support_bottom_stair_step_height
|
||||
support_conical_angle
|
||||
support_conical_enabled
|
||||
support_conical_min_width
|
||||
support_connect_zigzags
|
||||
support_join_distance
|
||||
support_minimal_diameter
|
||||
support_offset
|
||||
support_roof_enable
|
||||
support_roof_extruder_nr
|
||||
support_roof_height
|
||||
support_roof_line_distance
|
||||
support_roof_line_width
|
||||
support_roof_pattern
|
||||
support_top_distance
|
||||
support_tower_diameter
|
||||
support_tower_roof_angle
|
||||
switch_extruder_prime_speed
|
||||
top_bottom_pattern
|
||||
travel_avoid_distance
|
||||
travel_avoid_other_parts
|
||||
travel_compensate_overlapping_walls_enabled
|
||||
wall_line_width_0
|
||||
wall_line_width_x
|
||||
wireframe_bottom_delay
|
||||
wireframe_drag_along
|
||||
wireframe_enabled
|
||||
wireframe_fall_down
|
||||
wireframe_flat_delay
|
||||
wireframe_flow_connection
|
||||
wireframe_flow_flat
|
||||
wireframe_height
|
||||
wireframe_nozzle_clearance
|
||||
wireframe_printspeed_bottom
|
||||
wireframe_printspeed_down
|
||||
wireframe_printspeed_flat
|
||||
wireframe_printspeed_up
|
||||
wireframe_roof_drag_along
|
||||
wireframe_roof_fall_down
|
||||
wireframe_roof_inset
|
||||
wireframe_roof_outer_delay
|
||||
wireframe_straight_before_down
|
||||
wireframe_strategy
|
||||
wireframe_top_delay
|
||||
wireframe_top_jump
|
||||
wireframe_up_half_speed
|
||||
xy_offset
|
||||
z_seam_type
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef FAN_SPEED_LAYER_TIME_H
|
||||
#define FAN_SPEED_LAYER_TIME_H
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
struct FanSpeedLayerTimeSettings
|
||||
{
|
||||
public:
|
||||
double cool_min_layer_time;
|
||||
double cool_min_layer_time_fan_speed_max;
|
||||
double cool_fan_speed_min;
|
||||
double cool_fan_speed_max;
|
||||
double cool_min_speed;
|
||||
int cool_fan_full_layer;
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // FAN_SPEED_LAYER_TIME_H
|
||||
+351
-267
@@ -16,15 +16,30 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
|
||||
gcode.preSetup(storage.meshgroup);
|
||||
|
||||
gcode.resetTotalPrintTimeAndFilament();
|
||||
if (meshgroup_number == 1)
|
||||
{
|
||||
gcode.resetTotalPrintTimeAndFilament();
|
||||
}
|
||||
|
||||
if (command_socket)
|
||||
command_socket->beginGCode();
|
||||
|
||||
setConfigFanSpeedLayerTime();
|
||||
|
||||
setConfigCoasting(storage);
|
||||
|
||||
setConfigRetraction(storage);
|
||||
|
||||
|
||||
initConfigs(storage);
|
||||
|
||||
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
{ // skirt
|
||||
storage.skirt_config[extruder].setLayerHeight(getSettingInMicrons("layer_height_0"));
|
||||
}
|
||||
|
||||
|
||||
layer_plan_buffer.setPreheatConfig(*storage.meshgroup);
|
||||
|
||||
if (meshgroup_number == 1)
|
||||
{
|
||||
processStartingCode(storage);
|
||||
@@ -35,8 +50,13 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
}
|
||||
meshgroup_number++;
|
||||
|
||||
unsigned int total_layers = storage.meshes[0].layers.size();
|
||||
//gcode.writeComment("Layer count: %d", totalLayers);
|
||||
size_t total_layers = 0;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
total_layers = std::max(total_layers, mesh.layers.size());
|
||||
}
|
||||
|
||||
gcode.writeLayerCountComment(total_layers);
|
||||
|
||||
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
|
||||
if (has_raft)
|
||||
@@ -51,31 +71,30 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
{
|
||||
processLayer(storage, layer_nr, total_layers, has_raft);
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper, command_socket);
|
||||
|
||||
gcode.writeFanCommand(0);
|
||||
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper, command_socket);
|
||||
|
||||
//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
|
||||
max_object_height = std::max(max_object_height, storage.model_max.z);
|
||||
|
||||
if (command_socket)
|
||||
{
|
||||
finalize();
|
||||
command_socket->sendGCodeLayer();
|
||||
command_socket->endSendSlicedObject();
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
std::ostringstream prefix;
|
||||
prefix << ";FLAVOR:UltiGCode\n";
|
||||
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
|
||||
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
|
||||
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
|
||||
command_socket->sendGCodePrefix(prefix.str());
|
||||
}
|
||||
}
|
||||
|
||||
layer_plan_buffer.flush();
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigFanSpeedLayerTime()
|
||||
{
|
||||
fan_speed_layer_time_settings.cool_min_layer_time = getSettingInSeconds("cool_min_layer_time");
|
||||
fan_speed_layer_time_settings.cool_min_layer_time_fan_speed_max = getSettingInSeconds("cool_min_layer_time_fan_speed_max");
|
||||
fan_speed_layer_time_settings.cool_fan_speed_min = getSettingInPercentage("cool_fan_speed_min");
|
||||
fan_speed_layer_time_settings.cool_fan_speed_max = getSettingInPercentage("cool_fan_speed_max");
|
||||
fan_speed_layer_time_settings.cool_min_speed = getSettingInMillimetersPerSecond("cool_min_speed");
|
||||
fan_speed_layer_time_settings.cool_fan_full_layer = getSettingAsCount("cool_fan_full_layer");
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
|
||||
{
|
||||
@@ -87,127 +106,128 @@ void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
|
||||
coasting_config.coasting_enable = train->getSettingBoolean("coasting_enable");
|
||||
coasting_config.coasting_volume_move = train->getSettingInCubicMillimeters("coasting_volume_move");
|
||||
coasting_config.coasting_min_volume_move = train->getSettingInCubicMillimeters("coasting_min_volume_move");
|
||||
coasting_config.coasting_speed_move = train->getSettingInPercentage("coasting_speed_move");
|
||||
coasting_config.coasting_speed_move = train->getSettingInPercentage("coasting_speed_move") / 100.0;
|
||||
coasting_config.coasting_volume_retract = train->getSettingInCubicMillimeters("coasting_volume_retract");
|
||||
coasting_config.coasting_min_volume_retract = train->getSettingInCubicMillimeters("coasting_min_volume_retract");
|
||||
coasting_config.coasting_speed_retract = train->getSettingInPercentage("coasting_speed_retract");
|
||||
coasting_config.coasting_speed_retract = train->getSettingInPercentage("coasting_speed_retract") / 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigRetraction(SliceDataStorage& storage)
|
||||
{
|
||||
storage.retraction_config.amount = (storage.getSettingBoolean("retraction_enable"))? INT2MM(getSettingInMicrons("retraction_amount")) : 0;
|
||||
storage.retraction_config.primeAmount = INT2MM(getSettingInMicrons("retraction_extra_prime_amount"));
|
||||
storage.retraction_config.distance = (storage.getSettingBoolean("retraction_enable"))? INT2MM(getSettingInMicrons("retraction_amount")) : 0;
|
||||
storage.retraction_config.prime_volume = getSettingInCubicMillimeters("retraction_extra_prime_amount");
|
||||
storage.retraction_config.speed = getSettingInMillimetersPerSecond("retraction_retract_speed");
|
||||
storage.retraction_config.primeSpeed = getSettingInMillimetersPerSecond("retraction_prime_speed");
|
||||
storage.retraction_config.zHop = getSettingInMicrons("retraction_hop");
|
||||
storage.retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
|
||||
storage.retraction_config.retraction_extrusion_window = getSettingInMicrons("retraction_extrusion_window");
|
||||
storage.retraction_config.retraction_count_max = getSettingInMicrons("retraction_count_max");
|
||||
storage.retraction_config.retraction_extrusion_window = INT2MM(getSettingInMicrons("retraction_extrusion_window"));
|
||||
storage.retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
|
||||
|
||||
int extruder_count = storage.meshgroup->getExtruderCount();
|
||||
for (int extruder = 0; extruder < extruder_count; extruder++)
|
||||
{
|
||||
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder);
|
||||
storage.retraction_config_per_extruder[extruder].amount = (train->getSettingBoolean("retraction_enable"))? INT2MM(train->getSettingInMicrons("retraction_amount")) : 0;
|
||||
storage.retraction_config_per_extruder[extruder].primeAmount = INT2MM(train->getSettingInMicrons("retraction_extra_prime_amount"));
|
||||
storage.retraction_config_per_extruder[extruder].speed = train->getSettingInMillimetersPerSecond("retraction_retract_speed");
|
||||
storage.retraction_config_per_extruder[extruder].primeSpeed = train->getSettingInMillimetersPerSecond("retraction_prime_speed");
|
||||
storage.retraction_config_per_extruder[extruder].zHop = train->getSettingInMicrons("retraction_hop");
|
||||
storage.retraction_config_per_extruder[extruder].retraction_min_travel_distance = train->getSettingInMicrons("retraction_min_travel");
|
||||
storage.retraction_config_per_extruder[extruder].retraction_extrusion_window = train->getSettingInMicrons("retraction_extrusion_window");
|
||||
storage.retraction_config_per_extruder[extruder].retraction_count_max = train->getSettingInMicrons("retraction_count_max");
|
||||
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[extruder];
|
||||
retraction_config.distance = (train->getSettingBoolean("retraction_enable"))? INT2MM(train->getSettingInMicrons("retraction_amount")) : 0;
|
||||
retraction_config.prime_volume = train->getSettingInCubicMillimeters("retraction_extra_prime_amount");
|
||||
retraction_config.speed = train->getSettingInMillimetersPerSecond("retraction_retract_speed");
|
||||
retraction_config.primeSpeed = train->getSettingInMillimetersPerSecond("retraction_prime_speed");
|
||||
retraction_config.zHop = train->getSettingInMicrons("retraction_hop");
|
||||
retraction_config.retraction_min_travel_distance = train->getSettingInMicrons("retraction_min_travel");
|
||||
retraction_config.retraction_extrusion_window = INT2MM(train->getSettingInMicrons("retraction_extrusion_window"));
|
||||
retraction_config.retraction_count_max = train->getSettingAsCount("retraction_count_max");
|
||||
}
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh.retraction_config.amount = (mesh.getSettingBoolean("retraction_enable"))? INT2MM(mesh.getSettingInMicrons("retraction_amount")) : 0;
|
||||
mesh.retraction_config.primeAmount = INT2MM(mesh.getSettingInMicrons("retraction_extra_prime_amount"));
|
||||
mesh.retraction_config.distance = (mesh.getSettingBoolean("retraction_enable"))? INT2MM(mesh.getSettingInMicrons("retraction_amount")) : 0;
|
||||
mesh.retraction_config.prime_volume = mesh.getSettingInCubicMillimeters("retraction_extra_prime_amount");
|
||||
mesh.retraction_config.speed = mesh.getSettingInMillimetersPerSecond("retraction_retract_speed");
|
||||
mesh.retraction_config.primeSpeed = mesh.getSettingInMillimetersPerSecond("retraction_prime_speed");
|
||||
mesh.retraction_config.zHop = mesh.getSettingInMicrons("retraction_hop");
|
||||
mesh.retraction_config.retraction_min_travel_distance = mesh.getSettingInMicrons("retraction_min_travel");
|
||||
mesh.retraction_config.retraction_extrusion_window = mesh.getSettingInMicrons("retraction_extrusion_window");
|
||||
mesh.retraction_config.retraction_count_max = mesh.getSettingInMicrons("retraction_count_max");
|
||||
mesh.retraction_config.retraction_extrusion_window = INT2MM(mesh.getSettingInMicrons("retraction_extrusion_window"));
|
||||
mesh.retraction_config.retraction_count_max = mesh.getSettingAsCount("retraction_count_max");
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigSkirt(SliceDataStorage& storage, int layer_thickness)
|
||||
void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
|
||||
{
|
||||
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
{
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
|
||||
storage.skirt_config[extruder].setSpeed(train->getSettingInMillimetersPerSecond("skirt_speed"));
|
||||
storage.skirt_config[extruder].setLineWidth(train->getSettingInMicrons("skirt_line_width"));
|
||||
storage.skirt_config[extruder].setFlow(train->getSettingInPercentage("material_flow"));
|
||||
storage.skirt_config[extruder].setLayerHeight(layer_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigSupport(SliceDataStorage& storage, int layer_thickness)
|
||||
{
|
||||
storage.support_config.setLineWidth(getSettingInMicrons("support_line_width"));
|
||||
storage.support_config.setSpeed(getSettingInMillimetersPerSecond("speed_support_lines"));
|
||||
storage.support_config.setFlow(storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_extruder_nr"))->getSettingInPercentage("material_flow"));
|
||||
storage.support_config.setLayerHeight(layer_thickness);
|
||||
storage.travel_config.init(getSettingInMillimetersPerSecond("speed_travel"), 0, 0);
|
||||
|
||||
storage.support_roof_config.setLineWidth(getSettingInMicrons("support_roof_line_width"));
|
||||
storage.support_roof_config.setSpeed(getSettingInMillimetersPerSecond("speed_support_roof"));
|
||||
storage.support_roof_config.setFlow(storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_roof_extruder_nr"))->getSettingInPercentage("material_flow"));
|
||||
storage.support_roof_config.setLayerHeight(layer_thickness);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigInsets(SliceMeshStorage& mesh, int layer_thickness)
|
||||
{
|
||||
mesh.inset0_config.setLineWidth(mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
mesh.inset0_config.setSpeed(mesh.getSettingInMillimetersPerSecond("speed_wall_0"));
|
||||
mesh.inset0_config.setFlow(mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.inset0_config.setLayerHeight(layer_thickness);
|
||||
|
||||
mesh.insetX_config.setLineWidth(mesh.getSettingInMicrons("wall_line_width_x"));
|
||||
mesh.insetX_config.setSpeed(mesh.getSettingInMillimetersPerSecond("speed_wall_x"));
|
||||
mesh.insetX_config.setFlow(mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.insetX_config.setLayerHeight(layer_thickness);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigSkin(SliceMeshStorage& mesh, int layer_thickness)
|
||||
{
|
||||
mesh.skin_config.setLineWidth(mesh.getSettingInMicrons("skin_line_width"));
|
||||
mesh.skin_config.setSpeed(mesh.getSettingInMillimetersPerSecond("speed_topbottom"));
|
||||
mesh.skin_config.setFlow(mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.skin_config.setLayerHeight(layer_thickness);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigInfill(SliceMeshStorage& mesh, int layer_thickness)
|
||||
{
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].setLineWidth(mesh.getSettingInMicrons("infill_line_width") * (idx + 1));
|
||||
mesh.infill_config[idx].setSpeed(mesh.getSettingInMillimetersPerSecond("speed_infill"));
|
||||
mesh.infill_config[idx].setFlow(mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.infill_config[idx].setLayerHeight(layer_thickness);
|
||||
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
{ // skirt
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
|
||||
storage.skirt_config[extruder].init(train->getSettingInMillimetersPerSecond("skirt_speed"), train->getSettingInMicrons("skirt_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
}
|
||||
}
|
||||
|
||||
{ // support
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_infill_extruder_nr"));
|
||||
storage.support_config.init(getSettingInMillimetersPerSecond("speed_support_infill"), getSettingInMicrons("support_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
|
||||
storage.support_roof_config.init(getSettingInMillimetersPerSecond("speed_support_roof"), getSettingInMicrons("support_roof_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
}
|
||||
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh.inset0_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_0"), mesh.getSettingInMicrons("wall_line_width_0"), mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.insetX_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_x"), mesh.getSettingInMicrons("wall_line_width_x"), mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.skin_config.init(mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMicrons("skin_line_width"), mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.wall_reinforcement_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_reinforcement"), mesh.getSettingInMicrons("wall_reinforcement_line_width"), mesh.getSettingInPercentage("material_flow"));
|
||||
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].init(mesh.getSettingInMillimetersPerSecond("speed_infill"), mesh.getSettingInMicrons("infill_line_width") * (idx + 1), mesh.getSettingInPercentage("material_flow"));
|
||||
}
|
||||
mesh.wall_reinforcement_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_reinforcement"), mesh.getSettingInMicrons("wall_reinforcement_line_width"), mesh.getSettingInPercentage("material_flow"));
|
||||
}
|
||||
|
||||
storage.primeTower.initConfigs(storage.meshgroup, storage.retraction_config_per_extruder);
|
||||
}
|
||||
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
|
||||
{
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
if (!command_socket)
|
||||
{
|
||||
if (!command_socket)
|
||||
std::ostringstream prefix;
|
||||
prefix << "FLAVOR:" << toString(gcode.getFlavor());
|
||||
gcode.writeComment(prefix.str().c_str());
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
|
||||
gcode.writeComment("TIME:666");
|
||||
gcode.writeComment("MATERIAL:666");
|
||||
gcode.writeComment("MATERIAL2:-1");
|
||||
}
|
||||
}
|
||||
else
|
||||
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
|
||||
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), true);
|
||||
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
if (mesh.getSettingInDegreeCelsius("material_print_temperature") > 0)
|
||||
gcode.writeTemperatureCommand(mesh.getSettingAsIndex("extruder_nr"), mesh.getSettingInDegreeCelsius("material_print_temperature"));
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
if (mesh.getSettingInDegreeCelsius("material_print_temperature") > 0)
|
||||
gcode.writeTemperatureCommand(mesh.getSettingAsIndex("extruder_nr"), mesh.getSettingInDegreeCelsius("material_print_temperature"), true);
|
||||
if (getSettingBoolean("material_bed_temp_prepend"))
|
||||
{
|
||||
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
|
||||
{
|
||||
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), getSettingBoolean("material_bed_temp_wait"));
|
||||
}
|
||||
}
|
||||
|
||||
if (getSettingBoolean("material_print_temp_prepend"))
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (mesh.getSettingInDegreeCelsius("material_print_temperature") > 0)
|
||||
{
|
||||
gcode.writeTemperatureCommand(mesh.getSettingAsIndex("extruder_nr"), mesh.getSettingInDegreeCelsius("material_print_temperature"));
|
||||
}
|
||||
}
|
||||
if (getSettingBoolean("material_print_temp_wait"))
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (mesh.getSettingInDegreeCelsius("material_print_temperature") > 0)
|
||||
{
|
||||
gcode.writeTemperatureCommand(mesh.getSettingAsIndex("extruder_nr"), mesh.getSettingInDegreeCelsius("material_print_temperature"), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gcode.writeCode(getSettingString("machine_start_gcode").c_str());
|
||||
@@ -231,115 +251,126 @@ void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
|
||||
gcode.writeMove(Point(storage.model_min.x, storage.model_min.y), getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int totalLayers)
|
||||
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
int extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
|
||||
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
|
||||
|
||||
GCodePathConfig raft_base_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
|
||||
raft_base_config.setSpeed(getSettingInMillimetersPerSecond("raft_base_speed"));
|
||||
raft_base_config.setLineWidth(getSettingInMicrons("raft_base_line_width"));
|
||||
raft_base_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
|
||||
raft_base_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
GCodePathConfig raft_interface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
|
||||
raft_interface_config.setSpeed(getSettingInMillimetersPerSecond("raft_interface_speed"));
|
||||
raft_interface_config.setLineWidth(getSettingInMicrons("raft_interface_line_width"));
|
||||
raft_interface_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
|
||||
raft_interface_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
GCodePathConfig raft_surface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
|
||||
raft_surface_config.setSpeed(getSettingInMillimetersPerSecond("raft_surface_speed"));
|
||||
raft_surface_config.setLineWidth(getSettingInMicrons("raft_surface_line_width"));
|
||||
raft_surface_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
|
||||
raft_surface_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
bool retraction_combing = true;
|
||||
|
||||
int n_raft_surface_layers = train->getSettingAsCount("raft_surface_layers");
|
||||
|
||||
int z = 0;
|
||||
|
||||
{ // set configs
|
||||
storage.raft_base_config.init(train->getSettingInMillimetersPerSecond("raft_base_speed"), train->getSettingInMicrons("raft_base_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
storage.raft_base_config.setLayerHeight(train->getSettingInMicrons("raft_base_thickness"));
|
||||
|
||||
storage.raft_interface_config.init(train->getSettingInMillimetersPerSecond("raft_interface_speed"), train->getSettingInMicrons("raft_interface_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
storage.raft_interface_config.setLayerHeight(train->getSettingInMicrons("raft_interface_thickness"));
|
||||
|
||||
bool retraction_combing = false; // the raft isn't added to the parts to avoid
|
||||
storage.raft_surface_config.init(train->getSettingInMillimetersPerSecond("raft_surface_speed"), train->getSettingInMicrons("raft_surface_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
storage.raft_surface_config.setLayerHeight(train->getSettingInMicrons("raft_surface_thickness"));
|
||||
}
|
||||
|
||||
{ // raft base layer
|
||||
gcode.writeLayerComment(-3);
|
||||
gcode.writeComment("RAFT");
|
||||
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setCombing(false);
|
||||
|
||||
int layer_nr = -n_raft_surface_layers - 2;
|
||||
int layer_height = getSettingInMicrons("raft_base_thickness");
|
||||
z += layer_height;
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
|
||||
gcode_layer.setExtruder(extruder_nr);
|
||||
gcode.setZ(getSettingInMicrons("raft_base_thickness"));
|
||||
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &raft_base_config);
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, layer_height);
|
||||
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &storage.raft_base_config);
|
||||
|
||||
Polygons raftLines;
|
||||
int offset_from_poly_outline = 0;
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, train->getSettingInMicrons("raft_base_line_width"), train->getSettingInMicrons("raft_base_line_spacing"), train->getSettingInPercentage("infill_overlap"), 0);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &raft_base_config);
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), train->getSettingInPercentage("infill_overlap"), 0);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config);
|
||||
sendPolygons(SupportType, layer_nr, raftLines, storage.raft_base_config.getLineWidth());
|
||||
|
||||
gcode.writeFanCommand(train->getSettingInPercentage("raft_base_fan_speed"));
|
||||
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_base_thickness"));
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
|
||||
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_base_fan_speed"));
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
}
|
||||
|
||||
{ // raft interface layer
|
||||
gcode.writeLayerComment(-2);
|
||||
gcode.writeComment("RAFT");
|
||||
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setCombing(false);
|
||||
gcode.setZ(train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness"));
|
||||
|
||||
int layer_nr = -n_raft_surface_layers - 1;
|
||||
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
|
||||
z += layer_height;
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, layer_height);
|
||||
|
||||
Polygons raftLines;
|
||||
int offset_from_poly_outline = 0;
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, train->getSettingInMicrons("raft_interface_line_width"), train->getSettingInMicrons("raft_interface_line_spacing"), train->getSettingInPercentage("infill_overlap"), train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &raft_interface_config);
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), train->getSettingInPercentage("infill_overlap"), train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config);
|
||||
sendPolygons(SupportType, layer_nr, raftLines, storage.raft_interface_config.getLineWidth());
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
|
||||
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_interface_thickness"));
|
||||
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_interface_fan_speed"));
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
}
|
||||
|
||||
int layer_height = train->getSettingInMicrons("raft_surface_thickness");
|
||||
|
||||
for (int raftSurfaceLayer=1; raftSurfaceLayer<=train->getSettingAsCount("raft_surface_layers"); raftSurfaceLayer++)
|
||||
for (int raftSurfaceLayer=1; raftSurfaceLayer <= n_raft_surface_layers; raftSurfaceLayer++)
|
||||
{ // raft surface layers
|
||||
gcode.writeLayerComment(-1);
|
||||
gcode.writeComment("RAFT");
|
||||
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setCombing(false);
|
||||
gcode.setZ(train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness") + train->getSettingInMicrons("raft_surface_thickness")*raftSurfaceLayer);
|
||||
|
||||
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
|
||||
z += layer_height;
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, layer_height);
|
||||
|
||||
Polygons raft_lines;
|
||||
int offset_from_poly_outline = 0;
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raft_lines, train->getSettingInMicrons("raft_surface_line_width"), train->getSettingInMicrons("raft_surface_line_spacing"), train->getSettingInPercentage("infill_overlap"), 90 * raftSurfaceLayer);
|
||||
gcode_layer.addLinesByOptimizer(raft_lines, &raft_surface_config);
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raft_lines, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), train->getSettingInPercentage("infill_overlap"), 90 * raftSurfaceLayer);
|
||||
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config);
|
||||
sendPolygons(SupportType, layer_nr, raft_lines, storage.raft_surface_config.getLineWidth());
|
||||
|
||||
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_interface_thickness"));
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
|
||||
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_surface_fan_speed"));
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, command_socket);
|
||||
|
||||
|
||||
int layer_thickness = getSettingInMicrons("layer_height");
|
||||
if (layer_nr == 0 && !has_raft)
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
}
|
||||
|
||||
|
||||
|
||||
setConfigSkirt(storage, layer_thickness);
|
||||
|
||||
setConfigSupport(storage, layer_thickness);
|
||||
|
||||
storage.primeTower.setConfigs(storage.meshgroup, storage.retraction_config_per_extruder, layer_thickness);
|
||||
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
int max_nozzle_size = 0;
|
||||
std::vector<bool> extruders_used = storage.getExtrudersUsed(layer_nr);
|
||||
for (int extr_nr = 0; extr_nr < storage.meshgroup->getExtruderCount(); extr_nr++)
|
||||
{
|
||||
setConfigInsets(mesh, layer_thickness);
|
||||
setConfigSkin(mesh, layer_thickness);
|
||||
setConfigInfill(mesh, layer_thickness);
|
||||
if (extruders_used[extr_nr])
|
||||
{
|
||||
max_nozzle_size = std::max(max_nozzle_size, storage.meshgroup->getExtruderTrain(extr_nr)->getSettingInMicrons("machine_nozzle_size"));
|
||||
}
|
||||
}
|
||||
|
||||
processInitialLayersSpeedup(storage, layer_nr);
|
||||
|
||||
gcode.writeLayerComment(layer_nr);
|
||||
|
||||
int64_t comb_offset_from_outlines = storage.meshgroup->getExtruderTrain(gcode.getExtruderNr())->getSettingInMicrons("machine_nozzle_size") * 2; // TODO: only used when there is no second wall.
|
||||
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config, getSettingInMillimetersPerSecond("speed_travel"), getSettingBoolean("retraction_combing"), layer_nr, comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
int z = storage.meshes[0].layers[layer_nr].printZ;
|
||||
gcode.setZ(z);
|
||||
gcode.resetStartPosition();
|
||||
int64_t comb_offset_from_outlines = max_nozzle_size * 2;// TODO: only used when there is no second wall.
|
||||
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, getSettingBoolean("retraction_combing"), comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
@@ -357,22 +388,22 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
|
||||
|
||||
//Figure out in which order to print the meshes, do this by looking at the current extruder and preferer the meshes that use that extruder.
|
||||
std::vector<unsigned int> mesh_order = calculateMeshOrder(storage, gcode_layer.getExtruder());
|
||||
gcode_layer.setCombing(true);
|
||||
gcode_layer.setIsInside(true);
|
||||
for(unsigned int mesh_idx : mesh_order)
|
||||
{
|
||||
SliceMeshStorage* mesh = &storage.meshes[mesh_idx];
|
||||
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
|
||||
{
|
||||
gcode_layer.setCombing(false);
|
||||
gcode_layer.setIsInside(false);
|
||||
addMeshLayerToGCode_meshSurfaceMode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.setCombing(true); // needed when the last mesh was spiralized
|
||||
gcode_layer.setIsInside(true); // needed when the last mesh was spiralized
|
||||
addMeshLayerToGCode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
gcode_layer.setCombing(false);
|
||||
gcode_layer.setIsInside(false);
|
||||
|
||||
addSupportToGCode(storage, gcode_layer, layer_nr, extruder_nr_before, false);
|
||||
|
||||
@@ -381,41 +412,20 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
|
||||
int prev_extruder = gcode_layer.getExtruder(); // most likely the same extruder as we are extruding with now
|
||||
addPrimeTower(storage, gcode_layer, layer_nr, prev_extruder);
|
||||
}
|
||||
processFanSpeedAndMinimalLayerTime(storage, gcode_layer, layer_nr);
|
||||
|
||||
gcode_layer.writeGCode(getSettingBoolean("cool_lift_head"), layer_nr > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
|
||||
if (command_socket)
|
||||
command_socket->sendGCodeLayer();
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processInitialLayersSpeedup(SliceDataStorage& storage, unsigned int layer_nr)
|
||||
{
|
||||
double initial_speedup_layers = getSettingAsCount("speed_slowdown_layers");
|
||||
if (static_cast<int>(layer_nr) < initial_speedup_layers)
|
||||
{
|
||||
double initial_layer_speed = getSettingInMillimetersPerSecond("speed_layer_0");
|
||||
storage.support_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
initial_layer_speed = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
|
||||
mesh.inset0_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.insetX_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.skin_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
}
|
||||
}
|
||||
}
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processSkirt(SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int extruder_nr)
|
||||
{
|
||||
gcode_layer.setCombing(false);
|
||||
gcode_layer.setIsInside(false);
|
||||
Polygons& skirt = storage.skirt[extruder_nr];
|
||||
if (skirt.size() > 0)
|
||||
{
|
||||
gcode_layer.addTravel(skirt[skirt.size()-1].closestPointTo(gcode.getPositionXY()));
|
||||
gcode_layer.addTravel(skirt[skirt.size()-1].closestPointTo(gcode_layer.getLastPosition()));
|
||||
}
|
||||
gcode_layer.addPolygonsByOptimizer(skirt, &storage.skirt_config[extruder_nr]);
|
||||
|
||||
@@ -425,7 +435,7 @@ void FffGcodeWriter::processOozeShield(SliceDataStorage& storage, GCodePlanner&
|
||||
{
|
||||
if (storage.oozeShield.size() > 0)
|
||||
{
|
||||
gcode_layer.setCombing(false);
|
||||
gcode_layer.setIsInside(false);
|
||||
gcode_layer.addPolygonsByOptimizer(storage.oozeShield[layer_nr], &storage.skirt_config[0]); // TODO: skirt config idx should correspond to ooze shield extruder nr
|
||||
}
|
||||
}
|
||||
@@ -448,7 +458,7 @@ void FffGcodeWriter::processDraftShield(SliceDataStorage& storage, GCodePlanner&
|
||||
return;
|
||||
}
|
||||
|
||||
gcode_layer.setCombing(false);
|
||||
gcode_layer.setIsInside(false);
|
||||
gcode_layer.addPolygonsByOptimizer(storage.draft_protection_shield, &storage.skirt_config[0]); // TODO: skirt config idx should correspond to draft shield extruder nr
|
||||
|
||||
}
|
||||
@@ -543,7 +553,7 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
PathOrderOptimizer part_order_optimizer(gcode.getStartPositionXY(), z_seam_type);
|
||||
PathOrderOptimizer part_order_optimizer(last_position_planned, z_seam_type);
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
{
|
||||
part_order_optimizer.addPolygon(layer->parts[partNr].insets[0][0]);
|
||||
@@ -556,33 +566,54 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
{
|
||||
SliceLayerPart& part = layer->parts[order_idx];
|
||||
|
||||
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
|
||||
int infill_angle = 45;
|
||||
if (layer_nr & 1)
|
||||
if ((infill_pattern==EFillMethod::LINES || infill_pattern==EFillMethod::ZIG_ZAG) && layer_nr & 1)
|
||||
{
|
||||
infill_angle += 90;
|
||||
int extrusion_width = mesh->infill_config[0].getLineWidth();
|
||||
}
|
||||
int infill_line_width = mesh->infill_config[0].getLineWidth();
|
||||
|
||||
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
|
||||
double infill_overlap = mesh->getSettingInPercentage("infill_overlap");
|
||||
|
||||
int wall_reinforcement_line_distance = mesh->getSettingInMicrons("wall_reinforcement_line_distance");
|
||||
int wall_reinforcement_line_width = mesh->wall_reinforcement_config.getLineWidth();
|
||||
|
||||
if (mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
|
||||
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
for (unsigned int wall_idx = part.reinforcement_walls.size() - 1; int(wall_idx) >= 0; wall_idx--)
|
||||
{
|
||||
ReinforcementWall& reinforcement_wall = part.reinforcement_walls[wall_idx];
|
||||
processWallReinforcement(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width, true);
|
||||
}
|
||||
}
|
||||
|
||||
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type);
|
||||
|
||||
if (!mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
|
||||
for (ReinforcementWall& reinforcement_wall : part.reinforcement_walls)
|
||||
{
|
||||
processWallReinforcement(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width, false);
|
||||
}
|
||||
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
}
|
||||
|
||||
EFillMethod skin_pattern = mesh->getSettingAsFillMethod("top_bottom_pattern");
|
||||
int skin_angle = 45;
|
||||
if ((skin_pattern == EFillMethod::LINES || skin_pattern == EFillMethod::ZIG_ZAG) && layer_nr & 1)
|
||||
{
|
||||
skin_angle += 90; // should coincide with infill_angle (if both skin and infill are lines) so that the first top layer is orthogonal to the last infill layer
|
||||
}
|
||||
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
|
||||
infill_angle -= 45;
|
||||
skin_angle -= 45;
|
||||
|
||||
int64_t skin_overlap = 0;
|
||||
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, infill_angle, extrusion_width);
|
||||
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, skin_angle, mesh->skin_config.getLineWidth());
|
||||
|
||||
//After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
|
||||
if (!mesh->getSettingBoolean("magic_spiralize") || static_cast<int>(layer_nr) < mesh->getSettingAsCount("bottom_layers"))
|
||||
@@ -643,6 +674,54 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMe
|
||||
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processWallReinforcement(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width, bool inside_out)
|
||||
{
|
||||
if (wall_reinforcement_line_distance == 0 || (reinforcement_wall.wall_reinforcement_area.size() == 0 && reinforcement_wall.wall_reinforcement_axtra_walls.size() == 0) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (inside_out)
|
||||
{
|
||||
processWallReinforcement_extraWalls(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_width, inside_out);
|
||||
}
|
||||
|
||||
processWallReinforcement_infill(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width);
|
||||
|
||||
if (!inside_out)
|
||||
{
|
||||
processWallReinforcement_extraWalls(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_width, inside_out);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processWallReinforcement_extraWalls(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_width, bool inside_out)
|
||||
{
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() > 0)
|
||||
{
|
||||
for(int inset_number=reinforcement_wall.wall_reinforcement_axtra_walls.size()-1; inset_number>-1; inset_number--)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(reinforcement_wall.wall_reinforcement_axtra_walls[inset_number], &mesh->insetX_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
void FffGcodeWriter::processWallReinforcement_infill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width)
|
||||
{
|
||||
if (reinforcement_wall.wall_reinforcement_area.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Polygons infill_polygons;
|
||||
Polygons infill_lines;
|
||||
|
||||
EFillMethod pattern = mesh->getSettingAsFillMethod("wall_reinforcement_pattern");
|
||||
Infill infill_comp(pattern, reinforcement_wall.wall_reinforcement_area, 0, false, wall_reinforcement_line_width, wall_reinforcement_line_distance, infill_overlap, infill_angle, false, false);
|
||||
infill_comp.generate(infill_polygons, infill_lines, nullptr);
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->wall_reinforcement_config);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->wall_reinforcement_config);
|
||||
sendPolygons(SupportInfillType, layer_nr, infill_lines, wall_reinforcement_line_width);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type)
|
||||
{
|
||||
bool compensate_overlap = mesh->getSettingBoolean("travel_compensate_overlapping_walls_enabled");
|
||||
@@ -681,10 +760,11 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
|
||||
|
||||
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width)
|
||||
{
|
||||
Polygons skin_polygons;
|
||||
Polygons skin_lines;
|
||||
for(SkinPart& skin_part : part.skin_parts) // TODO: optimize parts order
|
||||
{
|
||||
Polygons skin_polygons;
|
||||
Polygons skin_lines;
|
||||
|
||||
EFillMethod pattern = mesh->getSettingAsFillMethod("top_bottom_pattern");
|
||||
int bridge = -1;
|
||||
if (layer_nr > 0)
|
||||
@@ -703,18 +783,20 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
{
|
||||
for (Polygons& skin_perimeter : skin_part.insets)
|
||||
{
|
||||
sendPolygons(SkinType, layer_nr, skin_perimeter, mesh->skin_config.getLineWidth());
|
||||
gcode_layer.addPolygonsByOptimizer(skin_perimeter, &mesh->skin_config); // add polygons to gcode in inward order
|
||||
}
|
||||
if (skin_part.insets.size() > 0)
|
||||
{
|
||||
inner_skin_outline = &skin_part.insets.back();
|
||||
offset_from_inner_skin_outline = -extrusion_width/2;
|
||||
if (mesh->getSettingString("fill_perimeter_gaps") != "Nowhere")
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
generateLineInfill(skin_part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inner_skin_outline == nullptr)
|
||||
{
|
||||
inner_skin_outline = &skin_part.outline;
|
||||
@@ -725,12 +807,16 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh->skin_config);
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config);
|
||||
sendPolygons(SkinType, layer_nr, skin_polygons, mesh->skin_config.getLineWidth());
|
||||
sendPolygons(SkinType, layer_nr, skin_lines, mesh->skin_config.getLineWidth());
|
||||
}
|
||||
|
||||
// handle gaps between perimeters etc.
|
||||
if (mesh->getSettingString("fill_perimeter_gaps") != "Nowhere")
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
generateLineInfill(part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
|
||||
Polygons perimeter_gap_lines;
|
||||
generateLineInfill(part.perimeterGaps, 0, perimeter_gap_lines, extrusion_width, extrusion_width, 0, infill_angle);
|
||||
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,39 +826,39 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
|
||||
return;
|
||||
|
||||
int support_roof_extruder_nr = getSettingAsIndex("support_roof_extruder_nr");
|
||||
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
|
||||
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
|
||||
|
||||
bool print_support_before_rest = support_extruder_nr == extruder_nr_before
|
||||
bool print_support_before_rest = support_infill_extruder_nr == extruder_nr_before
|
||||
|| support_roof_extruder_nr == extruder_nr_before;
|
||||
// TODO: always print support after rest when only one nozzle is used for the whole meshgroup
|
||||
|
||||
if (print_support_before_rest != before_rest)
|
||||
return;
|
||||
|
||||
gcode_layer.setCombing(false);
|
||||
gcode_layer.setIsInside(false);
|
||||
|
||||
int current_extruder_nr = gcode_layer.getExtruder();
|
||||
|
||||
if (storage.support.supportLayers[layer_nr].roofs.size() > 0)
|
||||
{
|
||||
if (support_roof_extruder_nr != support_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
|
||||
if (support_roof_extruder_nr != support_infill_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
|
||||
{
|
||||
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
|
||||
void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (!storage.support.generated
|
||||
|| layer_nr > storage.support.layer_nr_max_filled_layer
|
||||
@@ -786,17 +872,17 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
|
||||
EFillMethod support_pattern = getSettingAsFillMethod("support_pattern");
|
||||
if (layer_nr == 0 && (support_pattern == EFillMethod::LINES || support_pattern == EFillMethod::ZIG_ZAG)) { support_pattern = EFillMethod::GRID; }
|
||||
|
||||
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
|
||||
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
|
||||
|
||||
double infill_overlap = storage.meshgroup->getExtruderTrain(support_extruder_nr)->getSettingInPercentage("infill_overlap");
|
||||
double infill_overlap = storage.meshgroup->getExtruderTrain(support_infill_extruder_nr)->getSettingInPercentage("infill_overlap");
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_extruder_nr);
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_infill_extruder_nr);
|
||||
|
||||
Polygons& support = storage.support.supportLayers[layer_nr].supportAreas;
|
||||
|
||||
std::vector<PolygonsPart> support_islands = support.splitIntoParts();
|
||||
|
||||
PathOrderOptimizer island_order_optimizer(gcode.getPositionXY());
|
||||
PathOrderOptimizer island_order_optimizer(gcode_layer.getLastPosition());
|
||||
for(unsigned int n=0; n<support_islands.size(); n++)
|
||||
{
|
||||
island_order_optimizer.addPolygon(support_islands[n][0]);
|
||||
@@ -814,9 +900,14 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
|
||||
infill_comp.generate(support_polygons, support_lines, nullptr);
|
||||
|
||||
if (support_pattern == EFillMethod::GRID || support_pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(island, &storage.support_config);
|
||||
sendPolygons(SupportType, layer_nr, island, storage.support_config.getLineWidth());
|
||||
}
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config);
|
||||
sendPolygons(SupportInfillType, layer_nr, support_polygons, storage.support_config.getLineWidth());
|
||||
sendPolygons(SupportInfillType, layer_nr, support_lines, storage.support_config.getLineWidth());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -836,7 +927,11 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, roof_extruder_nr);
|
||||
|
||||
double fillAngle;
|
||||
if (getSettingInMicrons("support_roof_height") < 2 * getSettingInMicrons("layer_height"))
|
||||
if (pattern == EFillMethod::CONCENTRIC)
|
||||
{
|
||||
fillAngle = 0;
|
||||
}
|
||||
else if (getSettingInMicrons("support_roof_height") < 2 * getSettingInMicrons("layer_height") || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
fillAngle = 90; // perpendicular to support lines
|
||||
}
|
||||
@@ -852,8 +947,10 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
|
||||
Polygons support_lines;
|
||||
infill_comp.generate(support_polygons, support_lines, nullptr);
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config);
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_roof_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config);
|
||||
sendPolygons(SupportType, layer_nr, support_polygons, storage.support_roof_config.getLineWidth());
|
||||
sendPolygons(SupportType, layer_nr, support_lines, storage.support_roof_config.getLineWidth());
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
|
||||
@@ -890,48 +987,35 @@ void FffGcodeWriter::addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcod
|
||||
bool prime_tower_dir_outward = getSettingBoolean("prime_tower_dir_outward");
|
||||
bool wipe = getSettingBoolean("prime_tower_wipe_enabled");
|
||||
|
||||
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processFanSpeedAndMinimalLayerTime(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr)
|
||||
{
|
||||
double travelTime;
|
||||
double extrudeTime;
|
||||
gcodeLayer.getNaiveTimeEstimates(travelTime, extrudeTime);
|
||||
gcodeLayer.forceMinimalLayerTime(getSettingInSeconds("cool_min_layer_time"), getSettingInMillimetersPerSecond("cool_min_speed"), travelTime, extrudeTime);
|
||||
|
||||
// interpolate fan speed (for cool_fan_full_layer and for cool_min_layer_time_fan_speed_max)
|
||||
double fanSpeed = getSettingInPercentage("cool_fan_speed_min");
|
||||
double totalLayerTime = travelTime + extrudeTime;
|
||||
if (totalLayerTime < getSettingInSeconds("cool_min_layer_time"))
|
||||
{
|
||||
fanSpeed = getSettingInPercentage("cool_fan_speed_max");
|
||||
}
|
||||
else if (totalLayerTime < getSettingInSeconds("cool_min_layer_time_fan_speed_max"))
|
||||
{
|
||||
// when forceMinimalLayerTime didn't change the extrusionSpeedFactor, we adjust the fan speed
|
||||
double minTime = (getSettingInSeconds("cool_min_layer_time"));
|
||||
double maxTime = (getSettingInSeconds("cool_min_layer_time_fan_speed_max"));
|
||||
double fanSpeedMin = getSettingInPercentage("cool_fan_speed_min");
|
||||
double fanSpeedMax = getSettingInPercentage("cool_fan_speed_max");
|
||||
fanSpeed = fanSpeedMax - (fanSpeedMax-fanSpeedMin) * (totalLayerTime - minTime) / (maxTime - minTime);
|
||||
}
|
||||
if (static_cast<int>(layer_nr) < getSettingAsCount("cool_fan_full_layer"))
|
||||
{
|
||||
//Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0.
|
||||
fanSpeed = fanSpeed * layer_nr / getSettingAsCount("cool_fan_full_layer");
|
||||
}
|
||||
gcode.writeFanCommand(fanSpeed);
|
||||
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed, command_socket);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::finalize()
|
||||
{
|
||||
gcode.finalize(max_object_height, getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
if (command_socket)
|
||||
{
|
||||
std::ostringstream prefix;
|
||||
prefix << ";FLAVOR:" << toString(gcode.getFlavor()) << "\n";
|
||||
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
|
||||
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
|
||||
}
|
||||
command_socket->sendGCodePrefix(prefix.str());
|
||||
}
|
||||
|
||||
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
|
||||
gcode.writeTemperatureCommand(e, 0, false);
|
||||
|
||||
gcode.writeComment("End of Gcode");
|
||||
/*
|
||||
the profile string below can be executed since the M25 doesn't end the gcode on an UMO and when printing via USB.
|
||||
gcode.writeCode("M25 ;Stop reading from this point on.");
|
||||
gcode.writeComment("Cura profile string:");
|
||||
gcode.writeComment(FffProcessor::getInstance()->getAllLocalSettingsString() + FffProcessor::getInstance()->getProfileString());
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
+68
-30
@@ -5,6 +5,8 @@
|
||||
#include <fstream>
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "raft.h"
|
||||
#include "infill.h"
|
||||
@@ -13,8 +15,12 @@
|
||||
#include "gcodePlanner.h"
|
||||
#include "gcodeExport.h"
|
||||
#include "commandSocket.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "PrimeTower.h"
|
||||
#include "FanSpeedLayerTime.h"
|
||||
|
||||
|
||||
#include "LayerPlanBuffer.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -26,12 +32,15 @@ namespace cura
|
||||
*
|
||||
* The main function of this class is FffGcodeWriter::writeGCode().
|
||||
*/
|
||||
class FffGcodeWriter : public SettingsMessenger
|
||||
class FffGcodeWriter : public SettingsMessenger, NoCopy
|
||||
{
|
||||
friend class FffProcessor; // cause WireFrame2Gcode uses the member [gcode] (TODO)
|
||||
private:
|
||||
int max_object_height;
|
||||
int meshgroup_number; //!< used for sequential printing of objects
|
||||
|
||||
LayerPlanBuffer layer_plan_buffer;
|
||||
|
||||
GCodeExport gcode;
|
||||
CommandSocket* command_socket;
|
||||
std::ofstream output_file;
|
||||
@@ -42,9 +51,17 @@ private:
|
||||
* This is recorded per extruder to account for a prime tower per extruder, instead of the mixed prime tower.
|
||||
*/
|
||||
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
|
||||
|
||||
FanSpeedLayerTimeSettings fan_speed_layer_time_settings;
|
||||
|
||||
Point last_position_planned; //!< The position of the head before planning the next layer
|
||||
int current_extruder_planned; //!< The extruder train in use before planning the next layer
|
||||
public:
|
||||
FffGcodeWriter(SettingsBase* settings_)
|
||||
: SettingsMessenger(settings_)
|
||||
, layer_plan_buffer(this, command_socket, gcode)
|
||||
, last_position_planned(no_point)
|
||||
, current_extruder_planned(0) // TODO: make configurable
|
||||
{
|
||||
meshgroup_number = 1;
|
||||
max_object_height = 0;
|
||||
@@ -95,20 +112,19 @@ public:
|
||||
void writeGCode(SliceDataStorage& storage, TimeKeeper& timeKeeper);
|
||||
|
||||
private:
|
||||
void setConfigFanSpeedLayerTime();
|
||||
|
||||
void setConfigCoasting(SliceDataStorage& storage);
|
||||
|
||||
//Setup the retraction parameters.
|
||||
void setConfigRetraction(SliceDataStorage& storage);
|
||||
|
||||
void setConfigSkirt(SliceDataStorage& storage, int layer_thickness);
|
||||
/*!
|
||||
* initialize GcodePathConfig config parameters which don't change over all layers
|
||||
*/
|
||||
void initConfigs(SliceDataStorage& storage);
|
||||
|
||||
void setConfigSupport(SliceDataStorage& storage, int layer_thickness);
|
||||
|
||||
void setConfigInsets(SliceMeshStorage& mesh, int layer_thickness);
|
||||
|
||||
void setConfigSkin(SliceMeshStorage& mesh, int layer_thickness);
|
||||
|
||||
void setConfigInfill(SliceMeshStorage& mesh, int layer_thickness);
|
||||
void setConfigWallReinforcement(SliceMeshStorage& mesh, int layer_thickness);
|
||||
|
||||
/*!
|
||||
* Set temperatures and perform initial priming.
|
||||
@@ -125,25 +141,18 @@ private:
|
||||
/*!
|
||||
* Add raft gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param totalLayers The total number of layers.
|
||||
* \param total_layers The total number of layers.
|
||||
*/
|
||||
void processRaft(SliceDataStorage& storage, unsigned int totalLayers);
|
||||
void processRaft(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Add a layer to the gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param totalLayers The total number of layers.
|
||||
* \param total_layers The total number of layers.
|
||||
* \param has_raft Whether a raft is used for this print.
|
||||
*/
|
||||
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int totalLayers, bool has_raft);
|
||||
|
||||
/*!
|
||||
* Interpolate between the initial layer speeds and the eventual speeds.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void processInitialLayersSpeedup(SliceDataStorage& storage, unsigned int layer_nr);
|
||||
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft);
|
||||
|
||||
/*!
|
||||
* Add the skirt to the gcode.
|
||||
@@ -233,6 +242,43 @@ private:
|
||||
*/
|
||||
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
|
||||
|
||||
/*!
|
||||
* Add wall reinforcement for a given part in a layer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param reinforcement_wall The reinforcement wall for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param wall_reinforcement_line_distance The distance between the infill lines
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param wall_reinforcement_line_width extrusionWidth
|
||||
* \param inside_out Whether to print from inside outward or other way around
|
||||
*/
|
||||
void processWallReinforcement(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width, bool inside_out);
|
||||
|
||||
/*!
|
||||
* Add the inner extra walls of the wall reinforcement for a given part in a layer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param reinforcement_wall The reinforcement wall for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param wall_reinforcement_line_width extrusionWidth
|
||||
*/
|
||||
void processWallReinforcement_extraWalls(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_width, bool inside_out);
|
||||
|
||||
/*!
|
||||
* Add the infill of the wall reinforcement for a given part in a layer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param reinforcement_wall The reinforcement wall for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param wall_reinforcement_line_distance The distance between the infill lines
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param wall_reinforcement_line_width extrusionWidth
|
||||
*/
|
||||
void processWallReinforcement_infill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width);
|
||||
|
||||
/*!
|
||||
* Generate the insets for the walls of a given layer part.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
@@ -271,7 +317,7 @@ private:
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
void addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
/*!
|
||||
* Add the support roofs to the gcode of the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
@@ -301,14 +347,6 @@ private:
|
||||
*/
|
||||
void addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int prev_extruder);
|
||||
|
||||
/*!
|
||||
* Finish the layer by applying speed corrections for minimal layer times and determine the fanSpeed.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void processFanSpeedAndMinimalLayerTime(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add the end gcode and set all temperatures to zero.
|
||||
*/
|
||||
|
||||
+154
-162
@@ -1,9 +1,6 @@
|
||||
#include "FffPolygonGenerator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <random> // for bulging effect?
|
||||
#include <functional> // for bugling?
|
||||
#include <cmath> // for bulging?
|
||||
|
||||
#include "slicer.h"
|
||||
#include "utils/gettime.h"
|
||||
@@ -49,13 +46,28 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
|
||||
log("Slicing model...\n");
|
||||
int initial_layer_thickness = meshgroup->getSettingInMicrons("layer_height_0");
|
||||
if(initial_layer_thickness <= 0) //Initial layer height of 0 is not allowed. Negative layer height is nonsense.
|
||||
{
|
||||
logError("Initial layer height %i is disallowed.",initial_layer_thickness);
|
||||
return false;
|
||||
}
|
||||
int layer_thickness = meshgroup->getSettingInMicrons("layer_height");
|
||||
if(layer_thickness <= 0) //Layer height of 0 is not allowed. Negative layer height is nonsense.
|
||||
{
|
||||
logError("Layer height %i is disallowed.",layer_thickness);
|
||||
return false;
|
||||
}
|
||||
if (meshgroup->getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
|
||||
{
|
||||
initial_layer_thickness = layer_thickness;
|
||||
}
|
||||
int initial_slice_z = initial_layer_thickness - layer_thickness / 2;
|
||||
int layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
|
||||
if(layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
|
||||
{
|
||||
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper,commandSocket); //Continue directly with the inset stage, which will also immediately stop.
|
||||
return true; //This is NOT an error state!
|
||||
}
|
||||
|
||||
std::vector<Slicer*> slicerList;
|
||||
for(unsigned int mesh_idx = 0; mesh_idx < meshgroup->meshes.size(); mesh_idx++)
|
||||
@@ -79,9 +91,6 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper, commandSocket);
|
||||
|
||||
bulgeWalls(slicerList, meshgroup);
|
||||
|
||||
//carveMultipleVolumes(storage.meshes);
|
||||
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
|
||||
|
||||
@@ -99,20 +108,16 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
for(unsigned int layer_nr=0; layer_nr<meshStorage.layers.size(); layer_nr++)
|
||||
{
|
||||
SliceLayer& layer = meshStorage.layers[layer_nr];
|
||||
meshStorage.layers[layer_nr].printZ +=
|
||||
meshStorage.getSettingInMicrons("layer_height_0")
|
||||
- initial_slice_z;
|
||||
if (has_raft)
|
||||
{
|
||||
layer.printZ +=
|
||||
meshStorage.getSettingInMicrons("raft_base_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_interface_thickness")
|
||||
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("layer_height") //raft_surface_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_airgap")
|
||||
- initial_slice_z;
|
||||
}
|
||||
else
|
||||
{
|
||||
meshStorage.layers[layer_nr].printZ +=
|
||||
meshStorage.getSettingInMicrons("layer_height_0")
|
||||
- initial_slice_z;
|
||||
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("raft_surface_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_airgap");
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +128,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
|
||||
if (commandSocket)
|
||||
{
|
||||
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0 && !has_raft? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
|
||||
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,8 +141,12 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
|
||||
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper)
|
||||
{
|
||||
// const
|
||||
unsigned int total_layers = storage.meshes.at(0).layers.size();
|
||||
size_t total_layers = 0;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
total_layers = std::max<unsigned int>(total_layers, mesh.layers.size());
|
||||
}
|
||||
|
||||
//layerparts2HTML(storage, "output/output.html");
|
||||
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
|
||||
{
|
||||
@@ -157,6 +166,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);
|
||||
|
||||
AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
|
||||
/*
|
||||
if (storage.support.generated)
|
||||
{
|
||||
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
|
||||
@@ -165,6 +175,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
|
||||
int mesh_max_bottom_layer_count = 0;
|
||||
@@ -179,15 +190,20 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
{
|
||||
if (!getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
|
||||
{
|
||||
processSkins(storage, layer_number);
|
||||
processSkinsAndInfill(storage, layer_number);
|
||||
}
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket);
|
||||
}
|
||||
|
||||
for(unsigned int layer_number = total_layers-1; layer_number > 0; layer_number--)
|
||||
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
combineInfillLayers(layer_number, mesh, mesh.getSettingAsCount("infill_sparse_combine"));
|
||||
processWallReinforcement(storage, layer_number);
|
||||
}
|
||||
|
||||
unsigned int combined_infill_layers = storage.getSettingInMicrons("infill_sparse_thickness") / std::max(storage.getSettingInMicrons("layer_height"),1); //How many infill layers to combine to obtain the requested sparse thickness.
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
combineInfillLayers(mesh,combined_infill_layers);
|
||||
}
|
||||
|
||||
storage.primeTower.computePrimeTowerMax(storage);
|
||||
@@ -204,7 +220,18 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
{
|
||||
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
|
||||
{
|
||||
processFuzzySkin(mesh);
|
||||
processFuzzyWalls(mesh);
|
||||
}
|
||||
else if (mesh.getSettingAsCount("wall_line_count") > 0)
|
||||
{ // only send polygon data
|
||||
for (unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
|
||||
{
|
||||
SliceLayer* layer = &mesh.layers[layer_nr];
|
||||
for(SliceLayerPart& part : layer->parts)
|
||||
{
|
||||
sendPolygons(Inset0Type, layer_nr, (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0], mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,20 +256,12 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
|
||||
{
|
||||
if (layer->parts[partNr].insets.size() > 0)
|
||||
{
|
||||
sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_0);
|
||||
// sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_0); // done after processing fuzzy skin
|
||||
for(unsigned int inset=1; inset<layer->parts[partNr].insets.size(); inset++)
|
||||
sendPolygons(InsetXType, layer_nr, layer->parts[partNr].insets[inset], line_width_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // only send polygon data
|
||||
SliceLayer* layer = &mesh.layers[layer_nr];
|
||||
for(SliceLayerPart& part : layer->parts)
|
||||
{
|
||||
sendPolygons(Inset0Type, layer_nr, part.outline, mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
for (PolygonRef polyline : layer->openPolyLines)
|
||||
@@ -260,10 +279,75 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int totalLayers)
|
||||
|
||||
void FffPolygonGenerator::processWallReinforcement(SliceDataStorage& storage, unsigned int layer_nr)
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{ // generate infill area
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (mesh.getSettingInMicrons("wall_reinforcement_thickness") == 0.0 && mesh.getSettingAsCount("wall_reinforcement_line_count") == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int inset_count = mesh.getSettingAsCount("wall_reinforcement_line_count");
|
||||
int wall_line_width = mesh.getSettingInMicrons("wall_line_width_x");
|
||||
|
||||
SliceLayer* layer = &mesh.layers[layer_nr];
|
||||
for (SliceLayerPart& part : layer->parts)
|
||||
{
|
||||
if (part.infill_area.size() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int wall_reinforcement_count = mesh.getSettingAsCount("wall_reinforcement_count");
|
||||
part.reinforcement_walls.reserve(wall_reinforcement_count);
|
||||
|
||||
|
||||
for (unsigned int wall_idx = 0; int(wall_idx) < wall_reinforcement_count; wall_idx++)
|
||||
{
|
||||
part.reinforcement_walls.emplace_back();
|
||||
ReinforcementWall& reinforcement_wall = part.reinforcement_walls.back();
|
||||
|
||||
Polygons outer_wall_reinforcement_edge = part.infill_area[0].offset(-mesh.getSettingInMicrons("wall_reinforcement_thickness"));
|
||||
reinforcement_wall.wall_reinforcement_area = part.infill_area[0].difference(outer_wall_reinforcement_edge);
|
||||
if (mesh.getSettingAsCount("wall_reinforcement_line_count") > 0)
|
||||
{
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls.push_back(outer_wall_reinforcement_edge.offset(-wall_line_width/2));
|
||||
}
|
||||
else
|
||||
{
|
||||
part.infill_area[0] = outer_wall_reinforcement_edge.offset(-wall_line_width/2);
|
||||
}
|
||||
|
||||
// generate reinforcement wall extra walls
|
||||
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
generateWallReinforcementWallExtraWalls(&part, reinforcement_wall, wall_line_width, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() > 0)
|
||||
{
|
||||
part.infill_area[0] = reinforcement_wall.wall_reinforcement_axtra_walls.back().offset(-wall_line_width/2); // update the infill area to one reinforcement wall insetted (updated each time a reinforcement wall is generated)
|
||||
}
|
||||
if (part.insets.size() > 0)
|
||||
{
|
||||
for(Polygons& polys : reinforcement_wall.wall_reinforcement_axtra_walls)
|
||||
sendPolygons(SupportType, layer_nr, polys, wall_line_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers)
|
||||
{
|
||||
int n_empty_first_layers = 0;
|
||||
for (unsigned int layer_idx = 0; layer_idx < totalLayers; layer_idx++)
|
||||
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
|
||||
{
|
||||
bool layer_is_empty = true;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
@@ -297,50 +381,56 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int
|
||||
layer.printZ -= n_empty_first_layers * layer_height;
|
||||
}
|
||||
}
|
||||
totalLayers -= n_empty_first_layers;
|
||||
total_layers -= n_empty_first_layers;
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processSkins(SliceDataStorage& storage, unsigned int layer_nr)
|
||||
void FffPolygonGenerator::processSkinsAndInfill(SliceDataStorage& storage, unsigned int layer_nr)
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE) { continue; }
|
||||
|
||||
int extrusionWidth = mesh.getSettingInMicrons("wall_line_width_x");
|
||||
int wall_line_count = mesh.getSettingAsCount("wall_line_count");
|
||||
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
|
||||
int innermost_wall_extrusion_width = mesh.getSettingInMicrons("wall_line_width_x");
|
||||
int extrusionWidth_infill = mesh.getSettingInMicrons("infill_line_width");
|
||||
generateSkins(layer_nr, mesh, extrusionWidth, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
if (mesh.getSettingInMicrons("infill_line_distance") > 0)
|
||||
{
|
||||
int infill_skin_overlap = 0;
|
||||
if (mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") + 10)
|
||||
{
|
||||
infill_skin_overlap = extrusionWidth / 2;
|
||||
infill_skin_overlap = skin_extrusion_width / 2;
|
||||
}
|
||||
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap);
|
||||
if (mesh.getSettingString("fill_perimeter_gaps") == "Skin")
|
||||
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap, wall_line_count);
|
||||
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
|
||||
{
|
||||
generatePerimeterGaps(layer_nr, mesh, extrusionWidth, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
|
||||
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
|
||||
}
|
||||
else if (mesh.getSettingString("fill_perimeter_gaps") == "Everywhere")
|
||||
else if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::EVERYWHERE)
|
||||
{
|
||||
generatePerimeterGaps(layer_nr, mesh, extrusionWidth, 0, 0);
|
||||
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
for(SliceLayerPart& part : layer.parts)
|
||||
bool frontend_can_show_polygon_as_filled_polygon = false;
|
||||
if (frontend_can_show_polygon_as_filled_polygon)
|
||||
{
|
||||
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
|
||||
for (SkinPart& skin_part : part.skin_parts)
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
for(SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
sendPolygons(SkinType, layer_nr, skin_part.outline, extrusionWidth);
|
||||
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
|
||||
for (SkinPart& skin_part : part.skin_parts)
|
||||
{
|
||||
sendPolygons(SkinType, layer_nr, skin_part.outline, innermost_wall_extrusion_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int totalLayers)
|
||||
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
if (!getSettingBoolean("ooze_shield_enabled"))
|
||||
{
|
||||
@@ -349,28 +439,28 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned
|
||||
|
||||
int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist");
|
||||
|
||||
for(unsigned int layer_nr=0; layer_nr<totalLayers; layer_nr++)
|
||||
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist));
|
||||
}
|
||||
|
||||
int largest_printed_radius = MM2INT(1.0); // TODO: make var a parameter, and perhaps even a setting?
|
||||
for(unsigned int layer_nr=0; layer_nr<totalLayers; layer_nr++)
|
||||
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].offset(-largest_printed_radius).offset(largest_printed_radius);
|
||||
}
|
||||
int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height");//Allow for a 60deg angle in the oozeShield.
|
||||
for(unsigned int layer_nr=1; layer_nr<totalLayers; layer_nr++)
|
||||
for(unsigned int layer_nr=1; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr-1].offset(-allowed_angle_offset));
|
||||
}
|
||||
for(unsigned int layer_nr=totalLayers-1; layer_nr>0; layer_nr--)
|
||||
for(unsigned int layer_nr=total_layers-1; layer_nr>0; layer_nr--)
|
||||
{
|
||||
storage.oozeShield[layer_nr-1] = storage.oozeShield[layer_nr-1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int totalLayers)
|
||||
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
int draft_shield_height = getSettingInMicrons("draft_shield_height");
|
||||
int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
|
||||
@@ -387,7 +477,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned
|
||||
int layer_skip = 500 / layer_height + 1;
|
||||
|
||||
Polygons& draft_shield = storage.draft_protection_shield;
|
||||
for (unsigned int layer_nr = 0; layer_nr < totalLayers && layer_nr < max_screen_layer; layer_nr += layer_skip)
|
||||
for (unsigned int layer_nr = 0; layer_nr < total_layers && layer_nr < max_screen_layer; layer_nr += layer_skip)
|
||||
{
|
||||
draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
|
||||
}
|
||||
@@ -420,14 +510,19 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
|
||||
}
|
||||
|
||||
|
||||
void FffPolygonGenerator::processFuzzySkin(SliceMeshStorage& mesh)
|
||||
void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
|
||||
{
|
||||
if (mesh.getSettingAsCount("wall_line_count") == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int64_t fuzziness = mesh.getSettingInMicrons("magic_fuzzy_skin_thickness");
|
||||
int64_t avg_dist_between_points = mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
|
||||
int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
|
||||
int64_t range_random_point_dist = avg_dist_between_points / 2;
|
||||
for (SliceLayer& layer : mesh.layers)
|
||||
for (unsigned int layer_nr = 0; layer_nr < mesh.layers.size(); layer_nr++)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
for (SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
Polygons results;
|
||||
@@ -472,110 +567,7 @@ void FffPolygonGenerator::processFuzzySkin(SliceMeshStorage& mesh)
|
||||
}
|
||||
}
|
||||
skin = results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::bulgeWalls(std::vector< Slicer* > slicerList, MeshGroup* meshgroup)
|
||||
{
|
||||
|
||||
assert(slicerList.size() == meshgroup->meshes.size());
|
||||
for (unsigned int mesh_idx = 0; mesh_idx < slicerList.size(); mesh_idx++)
|
||||
{
|
||||
Slicer* slicer = slicerList[mesh_idx];
|
||||
Mesh& mesh = meshgroup->meshes[mesh_idx];
|
||||
|
||||
if (!mesh.getSettingBoolean("magic_bulge_walls"))
|
||||
{
|
||||
// continue; // TODO
|
||||
}
|
||||
|
||||
auto getBulging = [](Point xy, int z)
|
||||
{
|
||||
std::hash<int> hash_fn;
|
||||
int cell_size = MM2INT(0.2);
|
||||
int cell_dim = 5; // surrounding taken into account
|
||||
double result = 0.0;
|
||||
int bulging = MM2INT(10.0);
|
||||
Point3 middle(xy.X / cell_size, xy.Y / cell_size, z / cell_size);
|
||||
double total_weight = 0.0;
|
||||
for (int x = middle.x - cell_dim; x < middle.x + cell_dim; x++)
|
||||
{
|
||||
for (int y = middle.y - cell_dim; y < middle.y + cell_dim; y++)
|
||||
{
|
||||
for (int z = middle.z - cell_dim; z < middle.z + cell_dim; z++)
|
||||
{
|
||||
srand(x ^ (y << 8) ^ (z << 16)); // set seed
|
||||
int h = rand();
|
||||
// int h = hash_fn(x ^ (y << 8) ^ (z << 16));
|
||||
double r = (double(h % 200000 - 100000))/100000.0; // between -1 and 1
|
||||
double weight = sqrt(1.0 / (1.0 + static_cast<double>(((Point3(xy.X, xy.Y, z) - Point3(x,y,z)* cell_size)).vSize()) * 4));
|
||||
total_weight += weight;
|
||||
result += r * weight ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<int>(result / total_weight * bulging);
|
||||
// return rand() % (bulging*2) - bulging;
|
||||
};
|
||||
|
||||
int64_t avg_dist_between_points = MM2INT(0.5); // mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
|
||||
int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
|
||||
int64_t range_random_point_dist = avg_dist_between_points / 2;
|
||||
|
||||
int layer_height = mesh.getSettingInMicrons("layer_height");
|
||||
|
||||
for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
|
||||
{
|
||||
SlicerLayer& layer = slicer->layers[layer_nr];
|
||||
Polygons& outlines = layer.polygonList;
|
||||
Polygons results;
|
||||
|
||||
int z_approx = layer_nr * layer_height;
|
||||
|
||||
for (PolygonRef poly : outlines)
|
||||
{
|
||||
// generate points in between p0 and p1
|
||||
PolygonRef result = results.newPoly();
|
||||
|
||||
int64_t dist_left_over = rand() % (min_dist_between_points / 2); // the distance to be traversed on the line before making the first new point
|
||||
Point* p0 = &poly.back();
|
||||
for (Point& p1 : poly)
|
||||
{ // 'a' is the (next) new point between p0 and p1
|
||||
Point p0p1 = p1 - *p0;
|
||||
int64_t p0p1_size = vSize(p0p1);
|
||||
int64_t dist_last_point = dist_left_over + p0p1_size * 2; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
|
||||
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
|
||||
{
|
||||
Point in_between = *p0 + normal(p0p1, p0pa_dist);
|
||||
int r = getBulging(in_between, z_approx);
|
||||
Point perp_to_p0p1 = crossZ(p0p1);
|
||||
Point fuzz = normal(perp_to_p0p1, r);
|
||||
Point pa = in_between + fuzz;
|
||||
result.add(pa);
|
||||
dist_last_point = p0pa_dist;
|
||||
}
|
||||
dist_left_over = p0p1_size - dist_last_point;
|
||||
|
||||
p0 = &p1;
|
||||
}
|
||||
while (result.size() < 3 )
|
||||
{
|
||||
unsigned int point_idx = poly.size() - 2;
|
||||
result.add(poly[point_idx]);
|
||||
if (point_idx == 0) { break; }
|
||||
point_idx--;
|
||||
}
|
||||
if (result.size() < 3)
|
||||
{
|
||||
result.clear();
|
||||
for (Point& p : poly)
|
||||
result.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
outlines = results;
|
||||
sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-16
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/gettime.h"
|
||||
#include "settings.h"
|
||||
#include "sliceDataStorage.h"
|
||||
@@ -12,8 +13,6 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class Slicer; // forward declaration
|
||||
|
||||
/*!
|
||||
* Primary stage in Fused Filament Fabrication processing: Polygons are generated.
|
||||
* The model is sliced and each slice consists of polygons representing the outlines: the boundaries between inside and outside the object.
|
||||
@@ -23,7 +22,7 @@ class Slicer; // forward declaration
|
||||
*
|
||||
* The main function of this class is FffPolygonGenerator::generateAreas().
|
||||
*/
|
||||
class FffPolygonGenerator : public SettingsMessenger
|
||||
class FffPolygonGenerator : public SettingsMessenger, NoCopy
|
||||
{
|
||||
private:
|
||||
CommandSocket* commandSocket;
|
||||
@@ -91,9 +90,9 @@ private:
|
||||
* Remove all bottom layers which are empty.
|
||||
* \param storage Input and Ouput parameter: stores all layers
|
||||
* \param layer_height The height of each layer
|
||||
* \param totalLayers The total number of layers
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int totalLayers);
|
||||
void removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Generate the inset polygons which form the walls.
|
||||
@@ -102,27 +101,36 @@ private:
|
||||
*/
|
||||
void processInsets(SliceDataStorage& storage, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Generate the wall reinforcement extra wall polygons and its infill area which form the reinforcement.
|
||||
*
|
||||
* Also redefines the infill area;
|
||||
*
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param layer_nr The layer for which to generate the insets.
|
||||
*/
|
||||
void processWallReinforcement(SliceDataStorage& storage, unsigned int layer_nr);
|
||||
/*!
|
||||
* Generate the outline of the ooze shield.
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param totalLayers The total number of layers
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void processOozeShield(SliceDataStorage& storage, unsigned int totalLayers);
|
||||
void processOozeShield(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Generate the skin areas.
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param layer_nr The layer for which to generate the skin areas.
|
||||
*/
|
||||
void processSkins(SliceDataStorage& storage, unsigned int layer_nr);
|
||||
void processSkinsAndInfill(SliceDataStorage& storage, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Generate the polygons where the draft screen should be.
|
||||
*
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param totalLayers The total number of layers
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void processDraftShield(SliceDataStorage& storage, unsigned int totalLayers);
|
||||
void processDraftShield(SliceDataStorage& storage, unsigned int total_layers);
|
||||
/*!
|
||||
* Generate the skirt/brim/raft areas/insets.
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
@@ -132,15 +140,12 @@ private:
|
||||
|
||||
|
||||
/*!
|
||||
* Special mode: Make the outer wall 'fuzzy'
|
||||
* Make the outer wall 'fuzzy'
|
||||
*/
|
||||
void processFuzzySkin(SliceMeshStorage& mesh);
|
||||
void processFuzzyWalls(SliceMeshStorage& mesh);
|
||||
|
||||
|
||||
/*!
|
||||
* Special mode: bulge the outer walls
|
||||
*/
|
||||
void bulgeWalls(std::vector< Slicer* > slicerList, MeshGroup* meshgroup);
|
||||
|
||||
|
||||
};
|
||||
}//namespace cura
|
||||
|
||||
@@ -64,6 +64,15 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
|
||||
TimeKeeper time_keeper_total;
|
||||
|
||||
if (meshgroup->meshes.empty())
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
|
||||
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
|
||||
|
||||
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (meshgroup->getSettingBoolean("wireframe_enabled"))
|
||||
{
|
||||
log("starting Neith Weaver...\n");
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
private:
|
||||
FffPolygonGenerator polygon_generator;
|
||||
FffGcodeWriter gcode_writer;
|
||||
CommandSocket* command_socket;
|
||||
CommandSocket* command_socket; // TODO: replace all refs to command_socket by CommandSocket::getInstance()
|
||||
|
||||
bool first_meshgroup;
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef FLOW_TEMP_GRAPH
|
||||
#define FLOW_TEMP_GRAPH
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Class representing a graph matching a flow to a temperature.
|
||||
* The graph generally consists of several linear line segments between points at which the temperature and flow are matched.
|
||||
*/
|
||||
class FlowTempGraph
|
||||
{
|
||||
public:
|
||||
struct Datum
|
||||
{
|
||||
double flow; //!< The flow in mm^3/s
|
||||
double temp; //!< The temperature in *C
|
||||
Datum(double flow, double temp)
|
||||
: flow(flow)
|
||||
, temp(temp)
|
||||
{}
|
||||
};
|
||||
std::vector<Datum> data; //!< The points of the graph between which the graph is linearly interpolated
|
||||
|
||||
FlowTempGraph()
|
||||
{}
|
||||
|
||||
/*!
|
||||
* Get the temperature corresponding to a specific flow.
|
||||
*
|
||||
* For flows outside of the chart, the temperature at the minimal or maximal flow is returned.
|
||||
* When the graph is empty, the @p material_print_temperature is returned.
|
||||
*
|
||||
* \param flow the flow in mm^3/s
|
||||
* \param material_print_temperature The default printing temp (backward compatibility for when the graph fails)
|
||||
* \return the corresponding temp
|
||||
*/
|
||||
double getTemp(double flow, double material_print_temperature, bool flow_dependent_temperature)
|
||||
{
|
||||
if (!flow_dependent_temperature || data.size() == 0)
|
||||
{
|
||||
return material_print_temperature;
|
||||
}
|
||||
if (data.size() == 1)
|
||||
{
|
||||
return data.front().temp;
|
||||
}
|
||||
if (flow < data.front().flow)
|
||||
{
|
||||
logWarning("Warning! Flow too low!\n"); // TODO
|
||||
return data.front().temp;
|
||||
}
|
||||
Datum* last_datum = &data.front();
|
||||
for (unsigned int datum_idx = 1; datum_idx < data.size(); datum_idx++)
|
||||
{
|
||||
Datum& datum = data[datum_idx];
|
||||
if (datum.flow >= flow)
|
||||
{
|
||||
return last_datum->temp + (datum.temp - last_datum->temp) * (flow - last_datum->flow) / (datum.flow - last_datum->flow);
|
||||
}
|
||||
last_datum = &datum;
|
||||
}
|
||||
|
||||
logWarning("Warning! Flow too high!\n"); // TODO
|
||||
return data.back().temp;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // FLOW_TEMP_GRAPH
|
||||
@@ -0,0 +1,227 @@
|
||||
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "LayerPlanBuffer.h"
|
||||
#include "gcodeExport.h"
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
|
||||
|
||||
void LayerPlanBuffer::flush()
|
||||
{
|
||||
if (buffer.size() > 0)
|
||||
{
|
||||
insertPreheatCommands(); // insert preheat commands of the very last layer
|
||||
}
|
||||
for (GCodePlanner& layer_plan : buffer)
|
||||
{
|
||||
layer_plan.writeGCode(gcode, getSettingBoolean("cool_lift_head"), layer_plan.getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
|
||||
if (command_socket)
|
||||
command_socket->sendGCodeLayer();
|
||||
}
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
|
||||
{
|
||||
double acc_time = 0.0;
|
||||
for (unsigned int path_idx = 0; path_idx < extruder_plan_before.paths.size(); path_idx++)
|
||||
{
|
||||
GCodePath& path = extruder_plan_before.paths[path_idx];
|
||||
acc_time += path.estimates.getTotalTime();
|
||||
if (acc_time > time_after_extruder_plan_start)
|
||||
{
|
||||
// logError("Inserting %f\t seconds too early!\n", acc_time - time_after_extruder_plan_start);
|
||||
extruder_plan_before.insertCommand(path_idx, extruder, temp, false, acc_time - time_after_extruder_plan_start);
|
||||
return;
|
||||
}
|
||||
}
|
||||
extruder_plan_before.insertCommand(extruder_plan_before.paths.size(), extruder, temp, false); // insert at end of extruder plan if time_after_extruder_plan_start > extruder_plan.time
|
||||
// = special insert after all extruder plans
|
||||
}
|
||||
|
||||
double LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
|
||||
int extruder = extruder_plan.extruder;
|
||||
double required_temp = extruder_plan.required_temp;
|
||||
|
||||
unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
|
||||
bool first_it = true;
|
||||
double in_between_time = 0.0;
|
||||
for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
|
||||
{
|
||||
GCodePlanner& layer = *layers[layer_idx];
|
||||
if (!first_it)
|
||||
{
|
||||
extruder_plan_before_idx = layer.extruder_plans.size() - 1;
|
||||
}
|
||||
for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = layer.extruder_plans[extruder_plan_before_idx];
|
||||
if (extruder_plan.extruder == extruder)
|
||||
{
|
||||
return preheat_config.timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(in_between_time, extruder, required_temp);
|
||||
}
|
||||
in_between_time += extruder_plan.estimates.getTotalTime();
|
||||
}
|
||||
first_it = false;
|
||||
}
|
||||
// The last extruder plan with the same extruder falls outside of the buffer
|
||||
// assume the nozzle has cooled down to strandby temperature already.
|
||||
return preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(preheat_config.getStandbyTemp(extruder), extruder, required_temp, false);
|
||||
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp)
|
||||
{
|
||||
// time_before_extruder_plan_end is halved, so that at the layer change the temperature will be half way betewen the two requested temperatures
|
||||
double time_before_extruder_plan_end = 0.5 * preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(prev_extruder_plan.required_temp, extruder, required_temp, true);
|
||||
double time_after_extruder_plan_start = prev_extruder_plan.estimates.getTotalTime() - time_before_extruder_plan_end;
|
||||
if (time_after_extruder_plan_start < 0)
|
||||
{
|
||||
time_after_extruder_plan_start = 0; // don't override the extruder plan with same extruder of the previous layer
|
||||
}
|
||||
|
||||
insertPreheatCommand(prev_extruder_plan, time_after_extruder_plan_start, extruder, required_temp);
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
|
||||
int extruder = extruder_plan.extruder;
|
||||
double required_temp = extruder_plan.required_temp;
|
||||
|
||||
extruder_plan.insertCommand(0, extruder, required_temp, true); // just after the extruder switch, wait for the destination temperature to be reached
|
||||
|
||||
double time_before_extruder_plan_to_insert = timeBeforeExtruderPlanToInsert(layers, layer_plan_idx, extruder_plan_idx);
|
||||
|
||||
unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
|
||||
bool first_it = true; // Whether it's the first iteration of the for loop below
|
||||
for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
|
||||
{
|
||||
GCodePlanner& layer = *layers[layer_idx];
|
||||
if (!first_it)
|
||||
{
|
||||
extruder_plan_before_idx = layer.extruder_plans.size() - 1;
|
||||
}
|
||||
for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
|
||||
{
|
||||
ExtruderPlan& extruder_plan_before = layer.extruder_plans[extruder_plan_before_idx];
|
||||
assert (extruder_plan_before.extruder != extruder);
|
||||
|
||||
double time_here = extruder_plan_before.estimates.getTotalTime();
|
||||
if (time_here > time_before_extruder_plan_to_insert)
|
||||
{
|
||||
insertPreheatCommand(extruder_plan_before, time_here - time_before_extruder_plan_to_insert, extruder, required_temp);
|
||||
return;
|
||||
}
|
||||
time_before_extruder_plan_to_insert -= time_here;
|
||||
|
||||
}
|
||||
first_it = false;
|
||||
}
|
||||
|
||||
// time_before_extruder_plan_to_insert falls before all plans in the buffer
|
||||
ExtruderPlan& first_extruder_plan = layers[0]->extruder_plans[0];
|
||||
first_extruder_plan.insertCommand(0, extruder, required_temp, false); // insert preheat command at verfy beginning of buffer
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommand(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
|
||||
int extruder = extruder_plan.extruder;
|
||||
double required_temp = extruder_plan.required_temp;
|
||||
|
||||
|
||||
ExtruderPlan* prev_extruder_plan = nullptr;
|
||||
if (extruder_plan_idx == 0)
|
||||
{
|
||||
if (layer_plan_idx == 0)
|
||||
{ // the very first extruder plan
|
||||
for (int extruder_idx = 0; extruder_idx < getSettingAsCount("machine_extruder_count"); extruder_idx++)
|
||||
{ // set temperature of the first nozzle, turn other nozzles down
|
||||
if (extruder_idx == extruder)
|
||||
{
|
||||
// extruder_plan.insertCommand(0, extruder, required_temp, true);
|
||||
// the first used extruder should already be set to the required temp in the start gcode
|
||||
}
|
||||
else
|
||||
{
|
||||
extruder_plan.insertCommand(0, extruder_idx, preheat_config.getStandbyTemp(extruder_idx), false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev_extruder_plan = &layers[layer_plan_idx - 1]->extruder_plans.back();
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_extruder_plan = &layers[layer_plan_idx]->extruder_plans[extruder_plan_idx - 1];
|
||||
}
|
||||
assert(prev_extruder_plan != nullptr);
|
||||
|
||||
int prev_extruder = prev_extruder_plan->extruder;
|
||||
|
||||
if (prev_extruder != extruder)
|
||||
{ // set previous extruder to standby temperature
|
||||
prev_extruder_plan->insertCommand(prev_extruder_plan->paths.size(), prev_extruder, preheat_config.getStandbyTemp(prev_extruder), false);
|
||||
}
|
||||
|
||||
if (prev_extruder == extruder)
|
||||
{
|
||||
if (preheat_config.usesFlowDependentTemp(extruder))
|
||||
{
|
||||
insertPreheatCommand_singleExtrusion(*prev_extruder_plan, extruder, required_temp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
insertPreheatCommand_multiExtrusion(layers, layer_plan_idx, extruder_plan_idx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommands()
|
||||
{
|
||||
if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].paths.size() == 0))
|
||||
{ // disregard empty layer
|
||||
buffer.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<GCodePlanner*> layers;
|
||||
layers.reserve(buffer.size());
|
||||
for (GCodePlanner& layer_plan : buffer)
|
||||
{
|
||||
layers.push_back(&layer_plan);
|
||||
}
|
||||
|
||||
unsigned int layer_idx = layers.size() - 1;
|
||||
|
||||
// insert commands for all extruder plans on this layer
|
||||
GCodePlanner& layer_plan = *layers[layer_idx];
|
||||
for (unsigned int extruder_plan_idx = 0; extruder_plan_idx < layer_plan.extruder_plans.size(); extruder_plan_idx++)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = layer_plan.extruder_plans[extruder_plan_idx];
|
||||
double time = extruder_plan.estimates.getTotalUnretractedTime();
|
||||
if (time <= 0.0
|
||||
|| extruder_plan.estimates.getMaterial() == 0.0 // extruder plan only consists of moves (when an extruder switch occurs at the beginning of a layer)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
double avg_flow = extruder_plan.estimates.getMaterial() / time; // TODO: subtract retracted travel time
|
||||
extruder_plan.required_temp = preheat_config.getTemp(extruder_plan.extruder, avg_flow);
|
||||
|
||||
insertPreheatCommand(layers, layer_idx, extruder_plan_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,134 @@
|
||||
#ifndef LAYER_PLAN_BUFFER_H
|
||||
#define LAYER_PLAN_BUFFER_H
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "settings.h"
|
||||
#include "commandSocket.h"
|
||||
|
||||
#include "gcodeExport.h"
|
||||
#include "gcodePlanner.h"
|
||||
#include "MeshGroup.h"
|
||||
|
||||
#include "Preheat.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class LayerPlanBuffer : SettingsMessenger
|
||||
{
|
||||
CommandSocket* command_socket;
|
||||
|
||||
GCodeExport& gcode;
|
||||
|
||||
Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train.
|
||||
|
||||
static constexpr unsigned int buffer_size = 5; // should be as low as possible while still allowing enough time in the buffer to heat up from standby temp to printing temp // TODO: hardcoded value
|
||||
// this value should be higher than 1, cause otherwise each layer is viewed as the first layer and no temp commands are inserted.
|
||||
|
||||
public:
|
||||
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
|
||||
|
||||
LayerPlanBuffer(SettingsBaseVirtual* settings, CommandSocket* command_socket, GCodeExport& gcode)
|
||||
: SettingsMessenger(settings)
|
||||
, command_socket(command_socket)
|
||||
, gcode(gcode)
|
||||
{ }
|
||||
|
||||
void setPreheatConfig(MeshGroup& settings)
|
||||
{
|
||||
preheat_config.setConfig(settings);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Place a new layer plan (GcodePlanner) by constructing it with the given arguments.
|
||||
* Pop back the oldest layer plan is it exceeds the buffer size and write it to gcode.
|
||||
*/
|
||||
template<typename... Args>
|
||||
GCodePlanner& emplace_back(Args&&... constructor_args)
|
||||
{
|
||||
if (buffer.size() > 0)
|
||||
{
|
||||
insertPreheatCommands(); // insert preheat commands of the just completed layer plan (not the newly emplaced one)
|
||||
}
|
||||
buffer.emplace_back(constructor_args...);
|
||||
if (buffer.size() > buffer_size)
|
||||
{
|
||||
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
|
||||
if (command_socket)
|
||||
command_socket->sendGCodeLayer();
|
||||
buffer.pop_front();
|
||||
}
|
||||
return buffer.back();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Write all remaining layer plans (GCodePlanner) to gcode and empty the buffer.
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/*!
|
||||
* Insert the preheat command for @p extruder into @p extruder_plan_before
|
||||
*
|
||||
* \param extruder_plan_before An extruder plan before the extruder plan for which the temperature is computed, in which to insert the preheat command
|
||||
* \param time_after_extruder_plan_start The time after the start of the extruder plan, before which to insert the preheat command
|
||||
* \param extruder The extruder for which to set the temperature
|
||||
* \param temp The temperature of the preheat command
|
||||
*/
|
||||
void insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp);
|
||||
|
||||
/*!
|
||||
* Compute the time needed to preheat, based either on the time the extruder has been on standby
|
||||
* or based on the temp of the previous extruder plan which has the same extruder nr.
|
||||
*
|
||||
* \param layers The layers in the buffer, moved to a vector
|
||||
* \param layer_plan_idx The index into @p layers in which to find the extruder plan
|
||||
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to find the preheat time needed
|
||||
* \return the time needed to preheat
|
||||
*/
|
||||
double timeBeforeExtruderPlanToInsert(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
|
||||
|
||||
/*!
|
||||
* For two consecutive extruder plans of the same extruder (so on different layers),
|
||||
* preheat the extruder to the temperature corresponding to the average flow of the second extruder plan.
|
||||
*
|
||||
* The preheat commands are inserted such that the middle of the temperature change coincides with the start of the next layer.
|
||||
*
|
||||
* \param prev_extruder_plan The former extruder plan (of the former layer)
|
||||
* \param extruder The extruder for which too set the temperature
|
||||
* \param required_temp The required temperature for the second extruder plan
|
||||
*/
|
||||
void insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp);
|
||||
|
||||
/*!
|
||||
* Insert the preheat command for an extruder plan which is preceded by an extruder plan with a different extruder.
|
||||
* Find the time window in which this extruder hasn't been used
|
||||
* and compute at what time the preheat command needs to be inserted.
|
||||
* Then insert the preheat command in the right extruder plan.
|
||||
*
|
||||
* \param layers The layers in the buffer, moved to a vector
|
||||
* \param layer_plan_idx The index into @p layers in which to find the extruder plan
|
||||
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to find the preheat time needed
|
||||
*/
|
||||
void insertPreheatCommand_multiExtrusion(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
|
||||
|
||||
/*!
|
||||
* Insert the preheat command for the extruder plan corersponding to @p extruder_plan_idx of the layer corresponding to @p layer_plan_idx.
|
||||
*
|
||||
* \param layers The layers of the buffer, moved to a temporary vector (from lower to upper layers)
|
||||
* \param layer_plan_idx The index of the layer plan for which to generate a preheat command
|
||||
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to generate the preheat command
|
||||
*/
|
||||
void insertPreheatCommand(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
|
||||
|
||||
/*!
|
||||
* Insert the preheat commands for the last added layer (unless that layer was empty)
|
||||
*/
|
||||
void insertPreheatCommands();
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // LAYER_PLAN_BUFFER_H
|
||||
@@ -20,10 +20,8 @@ bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
|
||||
Point prev_middle;
|
||||
Point last_middle;
|
||||
int64_t line_width;
|
||||
|
||||
MergeInfillLines merger(gcode, paths, travelConfig, nozzle_size);
|
||||
|
||||
if (merger.isConvertible(path_idx, prev_middle, last_middle, line_width, false))
|
||||
|
||||
if (isConvertible(path_idx, prev_middle, last_middle, line_width, false))
|
||||
{
|
||||
// path_idx + 3 is the index of the second extrusion move to be converted in combination with the first
|
||||
{
|
||||
@@ -39,12 +37,15 @@ bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
|
||||
}
|
||||
|
||||
path_idx += 2;
|
||||
for (; merger.isConvertible(path_idx, prev_middle, last_middle, line_width, true); path_idx += 2)
|
||||
extruder_plan.handleInserts(path_idx, gcode);
|
||||
for (; isConvertible(path_idx, prev_middle, last_middle, line_width, true); path_idx += 2)
|
||||
{
|
||||
extruder_plan.handleInserts(path_idx, gcode);
|
||||
GCodePath& last_path = paths[path_idx + 3];
|
||||
writeCompensatedMove(last_middle, speed, last_path, line_width);
|
||||
}
|
||||
path_idx = path_idx + 1; // means that the next path considered is the travel path after the converted extrusion path corresponding to the updated path_idx
|
||||
extruder_plan.handleInserts(path_idx, gcode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -13,6 +13,8 @@ class MergeInfillLines
|
||||
// void merge(Point& from, Point& p0, Point& p1);
|
||||
GCodeExport& gcode; //!< Where to write the combined line to
|
||||
std::vector<GCodePath>& paths; //!< The paths currently under consideration
|
||||
ExtruderPlan& extruder_plan; //!< The extruder plan of the paths currently under consideration
|
||||
|
||||
GCodePathConfig& travelConfig; //!< The travel settings used to see whether a path is a travel path or an extrusion path
|
||||
int64_t nozzle_size; //!< The diameter of the hole in the nozzle
|
||||
|
||||
@@ -41,8 +43,8 @@ public:
|
||||
/*!
|
||||
* Simple constructor only used by MergeInfillLines::isConvertible to easily convey the environment
|
||||
*/
|
||||
MergeInfillLines(GCodeExport& gcode, std::vector<GCodePath>& paths, GCodePathConfig& travelConfig, int64_t nozzle_size)
|
||||
: gcode(gcode), paths(paths), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
|
||||
MergeInfillLines(GCodeExport& gcode, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size)
|
||||
: gcode(gcode), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
|
||||
|
||||
/*!
|
||||
* Check for lots of small moves and combine them into one large line.
|
||||
|
||||
+4
-7
@@ -140,15 +140,12 @@ bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x
|
||||
const char* ext = strrchr(filename, '.');
|
||||
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
|
||||
{
|
||||
if (object_parent_settings)
|
||||
Mesh mesh = object_parent_settings ? Mesh(object_parent_settings) : Mesh(meshgroup); //If we have object_parent_settings, use them as parent settings. Otherwise, just use meshgroup.
|
||||
if(loadMeshSTL(&mesh,filename,transformation)) //Load it! If successful...
|
||||
{
|
||||
meshgroup->meshes.emplace_back(object_parent_settings); // make new mesh with [object_parent_settings] as parent settings object
|
||||
meshgroup->meshes.push_back(mesh);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
meshgroup->meshes.emplace_back(meshgroup); // make new mesh with [meshgroup] as parent settings object
|
||||
}
|
||||
return loadMeshSTL(&meshgroup->meshes[meshgroup->meshes.size()-1], filename, transformation);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+14
-4
@@ -2,6 +2,7 @@
|
||||
#ifndef MESH_GROUP_H
|
||||
#define MESH_GROUP_H
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "mesh.h"
|
||||
#include "ExtruderTrain.h"
|
||||
|
||||
@@ -14,7 +15,7 @@ namespace cura
|
||||
* One MeshGroup is a whole which is printed at once.
|
||||
* Generally there is one single MeshGroup, though when using one-at-a-time printing, multiple MeshGroups are processed consecutively.
|
||||
*/
|
||||
class MeshGroup : public SettingsBase
|
||||
class MeshGroup : public SettingsBase, NoCopy
|
||||
{
|
||||
ExtruderTrain* extruders[MAX_EXTRUDERS] = {nullptr};
|
||||
int extruder_count;
|
||||
@@ -28,9 +29,9 @@ public:
|
||||
return extruder_count;
|
||||
}
|
||||
|
||||
MeshGroup(SettingsBaseVirtual* settings_base)
|
||||
MeshGroup(SettingsBaseVirtual* settings_base)
|
||||
: SettingsBase(settings_base)
|
||||
, extruder_count(-1)
|
||||
, extruder_count(-1)
|
||||
{}
|
||||
|
||||
~MeshGroup()
|
||||
@@ -44,7 +45,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
ExtruderTrain* getExtruderTrain(unsigned int extruder_nr)
|
||||
/*!
|
||||
* Create a new extruder train for the @p extruder_nr, or return the one which already exists.
|
||||
*/
|
||||
ExtruderTrain* createExtruderTrain(unsigned int extruder_nr)
|
||||
{
|
||||
if (!extruders[extruder_nr])
|
||||
{
|
||||
@@ -53,6 +57,12 @@ public:
|
||||
return extruders[extruder_nr];
|
||||
}
|
||||
|
||||
ExtruderTrain* getExtruderTrain(unsigned int extruder_nr)
|
||||
{
|
||||
assert(extruders[extruder_nr]);
|
||||
return extruders[extruder_nr];
|
||||
}
|
||||
|
||||
std::vector<Mesh> meshes;
|
||||
|
||||
Point3 min() //! minimal corner of bounding box
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
#ifndef PREHEAT_H
|
||||
#define PREHEAT_H
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm> // max
|
||||
|
||||
#include "utils/logoutput.h"
|
||||
#include "MeshGroup.h"
|
||||
|
||||
#include "FlowTempGraph.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
* Class for computing heatup and cooldown times used for computing the time the printer needs to heat up to a printing temperature.
|
||||
*/
|
||||
class Preheat
|
||||
{
|
||||
/*!
|
||||
* The nozzle and material temperature settings for an extruder train.
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
double time_to_heatup_1_degree; //!< average time it takes to heat up one degree (in the range of normal print temperatures and standby temperature)
|
||||
double time_to_cooldown_1_degree; //!< average time it takes to cool down one degree (in the range of normal print temperatures and standby temperature)
|
||||
|
||||
double heatup_cooldown_time_mod_while_printing; //!< The time to be added to Preheat::time_to_heatup_1_degree and subtracted from Preheat::time_to_cooldown_1_degree to get the timings while printing
|
||||
|
||||
double standby_temp; //!< The temperature at which the nozzle rests when it is not printing.
|
||||
|
||||
double material_print_temperature; //!< default print temp (backward compatilibily)
|
||||
|
||||
bool flow_dependent_temperature; //!< Whether to make the temperature dependent on flow
|
||||
|
||||
FlowTempGraph flow_temp_graph; //!< The graph linking flows to corresponding temperatures
|
||||
};
|
||||
|
||||
std::vector<Config> config_per_extruder;//!< the nozzle and material temperature settings for each extruder train.
|
||||
public:
|
||||
/*!
|
||||
* Get the standby temperature of an extruder train
|
||||
* \param extruder the extruder train for which to get the standby tmep
|
||||
* \return the standby temp
|
||||
*/
|
||||
double getStandbyTemp(int extruder)
|
||||
{
|
||||
return config_per_extruder[extruder].standby_temp;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the nozzle and material temperature settings for each extruder train.
|
||||
*/
|
||||
void setConfig(MeshGroup& settings)
|
||||
{
|
||||
for (int extruder_nr = 0; extruder_nr < settings.getExtruderCount(); extruder_nr++)
|
||||
{
|
||||
assert(settings.getExtruderTrain(extruder_nr) != nullptr);
|
||||
ExtruderTrain& extruder_train = *settings.getExtruderTrain(extruder_nr);
|
||||
config_per_extruder.emplace_back();
|
||||
Config& config = config_per_extruder.back();
|
||||
config.time_to_cooldown_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_cool_down_speed"); // 0.5
|
||||
config.time_to_heatup_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_heat_up_speed"); // 0.5
|
||||
config.heatup_cooldown_time_mod_while_printing = 1.0 / extruder_train.getSettingInSeconds("material_extrusion_cool_down_speed"); // 0.1
|
||||
config.standby_temp = extruder_train.getSettingInSeconds("material_standby_temperature"); // 150
|
||||
|
||||
config.material_print_temperature = extruder_train.getSettingInDegreeCelsius("material_print_temperature"); // 220
|
||||
|
||||
config.flow_dependent_temperature = extruder_train.getSettingBoolean("material_flow_dependent_temperature");
|
||||
|
||||
config.flow_temp_graph = extruder_train.getSettingAsFlowTempGraph("material_flow_temp_graph"); // [[0.1,180],[20,230]]
|
||||
}
|
||||
}
|
||||
|
||||
bool usesFlowDependentTemp(int extruder_nr)
|
||||
{
|
||||
return config_per_extruder[extruder_nr].flow_dependent_temperature;
|
||||
}
|
||||
private:
|
||||
/*!
|
||||
* Calculate time to heat up from standby temperature to a given temperature.
|
||||
* Assumes @p temp is higher than the standby temperature.
|
||||
*
|
||||
* \param extruder The extruder for which to get the time
|
||||
* \param temp The temperature to be reached
|
||||
*/
|
||||
double timeToHeatFromStandbyToPrintTemp(unsigned int extruder, double temp)
|
||||
{
|
||||
return (temp - config_per_extruder[extruder].standby_temp) * config_per_extruder[extruder].time_to_heatup_1_degree;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Get the optimal temperature corresponding to a given average flow.
|
||||
* \param extruder The extruder train
|
||||
* \param flow The flow for which to get the optimal temperature
|
||||
* \return The corresponding optimal temperature
|
||||
*/
|
||||
double getTemp(unsigned int extruder, double flow)
|
||||
{
|
||||
return config_per_extruder[extruder].flow_temp_graph.getTemp(flow, config_per_extruder[extruder].material_print_temperature, config_per_extruder[extruder].flow_dependent_temperature);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Decide when to start warming up again after starting to cool down towards the standby temperature.
|
||||
* Two cases are considered:
|
||||
* the case where the standby temperature is reached \__/ .
|
||||
* and the case where it isn't \/ .
|
||||
*
|
||||
* IT is assumed that the printer is not printing during this cool down and warm up time.
|
||||
*
|
||||
* Assumes from_temp is approximately the same as @p temp
|
||||
*
|
||||
* \param window_time The time window within which the cooldown and heat up must take place.
|
||||
* \param extruder The extruder used
|
||||
* \param temp The temperature to which to heat
|
||||
* \return The time before the end of the @p time_window to insert the preheat command
|
||||
*/
|
||||
double timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(double time_window, unsigned int extruder, double temp)
|
||||
{
|
||||
double time_ratio_cooldown_heatup = config_per_extruder[extruder].time_to_cooldown_1_degree / config_per_extruder[extruder].time_to_heatup_1_degree;
|
||||
double time_to_heat_from_standby_to_print_temp = timeToHeatFromStandbyToPrintTemp(extruder, temp);
|
||||
double time_needed_to_reach_standby_temp = time_to_heat_from_standby_to_print_temp * (1.0 + time_ratio_cooldown_heatup);
|
||||
if (time_needed_to_reach_standby_temp < time_window)
|
||||
{
|
||||
return time_to_heat_from_standby_to_print_temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return time_window * config_per_extruder[extruder].time_to_heatup_1_degree / (config_per_extruder[extruder].time_to_cooldown_1_degree + config_per_extruder[extruder].time_to_heatup_1_degree);
|
||||
}
|
||||
}
|
||||
/*!
|
||||
* Calculate time needed to warm up the nozzle from a given temp to a given temp.
|
||||
* If the printer is printing in the mean time the warming up will take longer.
|
||||
*
|
||||
*
|
||||
* \param from_temp The temperature at which the nozzle was before
|
||||
* \param extruder The extruder used
|
||||
* \param temp The temperature to which to heat
|
||||
* \param printing Whether the printer is printing in the time to heat up the nozzle
|
||||
* \return The time needed to reach the desired temperature (@p temp)
|
||||
*/
|
||||
double timeBeforeEndToInsertPreheatCommand_warmUp(double from_temp, unsigned int extruder, double temp, bool printing)
|
||||
{
|
||||
if (temp > from_temp)
|
||||
{
|
||||
if (printing)
|
||||
{
|
||||
return (temp - from_temp) * (config_per_extruder[extruder].time_to_heatup_1_degree + config_per_extruder[extruder].heatup_cooldown_time_mod_while_printing);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (temp - from_temp) * config_per_extruder[extruder].time_to_heatup_1_degree;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (printing)
|
||||
{
|
||||
return (from_temp - temp) * config_per_extruder[extruder].time_to_cooldown_1_degree;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (from_temp - temp) * std::max(0.0, config_per_extruder[extruder].time_to_cooldown_1_degree - config_per_extruder[extruder].heatup_cooldown_time_mod_while_printing);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // PREHEAT_H
|
||||
+33
-21
@@ -16,20 +16,29 @@ PrimeTower::PrimeTower()
|
||||
|
||||
|
||||
|
||||
void PrimeTower::setConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder, int layer_thickness)
|
||||
void PrimeTower::initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder)
|
||||
{
|
||||
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
|
||||
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], "SUPPORT");// so that visualization in the old Cura still works (TODO)
|
||||
}
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
ExtruderTrain* train = meshgroup->getExtruderTrain(extr);
|
||||
config_per_extruder[extr].init(train->getSettingInMillimetersPerSecond("speed_prime_tower"), train->getSettingInMicrons("prime_tower_line_width"), train->getSettingInPercentage("prime_tower_flow"));
|
||||
}
|
||||
}
|
||||
|
||||
void PrimeTower::setConfigs(MeshGroup* meshgroup, int layer_thickness)
|
||||
{
|
||||
|
||||
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
|
||||
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
ExtruderTrain* train = meshgroup->getExtruderTrain(extr);
|
||||
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], "WALL-INNER");// so that visualization in the old Cura still works (TODO)
|
||||
GCodePathConfig& conf = config_per_extruder.back();
|
||||
|
||||
conf.setSpeed(train->getSettingInMillimetersPerSecond("speed_prime_tower"));
|
||||
conf.setLineWidth(train->getSettingInMicrons("prime_tower_line_width"));
|
||||
conf.setFlow(train->getSettingInPercentage("prime_tower_flow"));
|
||||
GCodePathConfig& conf = config_per_extruder[extr];
|
||||
conf.setLayerHeight(layer_thickness);
|
||||
}
|
||||
}
|
||||
@@ -41,18 +50,18 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
|
||||
|
||||
extruder_count = storage.getSettingAsCount("machine_extruder_count");
|
||||
|
||||
int max_object_height_per_extruder[extruder_count];
|
||||
int max_object_height_per_extruder[extruder_count];
|
||||
std::fill_n(max_object_height_per_extruder, extruder_count, -1); // unitialize all as -1
|
||||
{ // compute max_object_height_per_extruder
|
||||
memset(max_object_height_per_extruder, -1, sizeof(max_object_height_per_extruder));
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")] =
|
||||
std::max( max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")]
|
||||
, mesh.layer_nr_max_filled_layer );
|
||||
}
|
||||
int support_extruder_nr = storage.getSettingAsIndex("support_extruder_nr"); // TODO: support extruder should be configurable per object
|
||||
max_object_height_per_extruder[support_extruder_nr] =
|
||||
std::max( max_object_height_per_extruder[support_extruder_nr]
|
||||
int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); // TODO: support extruder should be configurable per object
|
||||
max_object_height_per_extruder[support_infill_extruder_nr] =
|
||||
std::max( max_object_height_per_extruder[support_infill_extruder_nr]
|
||||
, storage.support.layer_nr_max_filled_layer );
|
||||
int support_roof_extruder_nr = storage.getSettingAsIndex("support_roof_extruder_nr"); // TODO: support roof extruder should be configurable per object
|
||||
max_object_height_per_extruder[support_roof_extruder_nr] =
|
||||
@@ -72,7 +81,7 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
|
||||
for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
|
||||
{
|
||||
if (extruder_nr == extruder_max_object_height) { continue; }
|
||||
if (max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_second_max_object_height])
|
||||
if (extruder_second_max_object_height == -1 || max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_second_max_object_height])
|
||||
{
|
||||
extruder_second_max_object_height = extruder_nr;
|
||||
}
|
||||
@@ -104,7 +113,7 @@ void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
|
||||
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
|
||||
}
|
||||
|
||||
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int totalLayers)
|
||||
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0
|
||||
// && storage.getSettingInMicrons("prime_tower_distance") > 0
|
||||
@@ -113,7 +122,7 @@ void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int totalLaye
|
||||
generatePaths3(storage);
|
||||
}
|
||||
}
|
||||
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int totalLayers)
|
||||
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_distance") > 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
@@ -177,7 +186,7 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
|
||||
|
||||
|
||||
|
||||
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
|
||||
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
|
||||
{
|
||||
if (!( storage.max_object_height_second_to_last_extruder >= 0
|
||||
// && storage.getSettingInMicrons("prime_tower_distance") > 0
|
||||
@@ -199,10 +208,10 @@ void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer,
|
||||
{
|
||||
wipe = false;
|
||||
}
|
||||
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
|
||||
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed, command_socket);
|
||||
}
|
||||
|
||||
void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
|
||||
void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
|
||||
{
|
||||
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
|
||||
{
|
||||
@@ -221,14 +230,17 @@ void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer
|
||||
gcodeLayer.addLinesByOptimizer(pattern, &config);
|
||||
|
||||
last_prime_tower_poly_printed[new_extruder] = layer_nr;
|
||||
|
||||
|
||||
if (command_socket)
|
||||
command_socket->sendPolygons(SupportType, layer_nr, pattern, config.getLineWidth());
|
||||
|
||||
if (wipe)
|
||||
{ //Make sure we wipe the old extruder on the prime tower.
|
||||
gcodeLayer.addTravel(storage.wipePoint - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
|
||||
}
|
||||
}
|
||||
|
||||
void PrimeTower::addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
|
||||
void PrimeTower::addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
|
||||
{
|
||||
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
|
||||
{
|
||||
|
||||
+8
-7
@@ -26,7 +26,8 @@ private:
|
||||
|
||||
};
|
||||
public:
|
||||
void setConfigs(MeshGroup* configs, std::vector<RetractionConfig>& retraction_config_per_extruder, int layer_thickness);
|
||||
void initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder);
|
||||
void setConfigs(MeshGroup* configs, int layer_thickness);
|
||||
|
||||
Polygons ground_poly;
|
||||
|
||||
@@ -44,18 +45,18 @@ public:
|
||||
* Generate the area where the prime tower should be.
|
||||
*
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param totalLayers The total number of layers
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void generatePaths(SliceDataStorage& storage, unsigned int totalLayers);
|
||||
void generatePaths_OLD(SliceDataStorage& storage, unsigned int totalLayers);
|
||||
void generatePaths(SliceDataStorage& storage, unsigned int total_layers);
|
||||
void generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
void computePrimeTowerMax(SliceDataStorage& storage);
|
||||
|
||||
PrimeTower();
|
||||
|
||||
void addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
|
||||
void addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
|
||||
void addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
|
||||
void addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
|
||||
void addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
|
||||
void addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
enum class EPrintFeature : unsigned int
|
||||
{
|
||||
enum class EPrintFeature : unsigned int // unused!!
|
||||
{ // TODO: use in gcodePathConfigs ?
|
||||
OUTER_WALL,
|
||||
INNER_WALLS,
|
||||
INFILL,
|
||||
|
||||
+19
-8
@@ -28,7 +28,6 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
slicerList.push_back(slicer);
|
||||
}
|
||||
|
||||
|
||||
int starting_layer_idx;
|
||||
{ // find first non-empty layer
|
||||
for (starting_layer_idx = 0; starting_layer_idx < layer_count; starting_layer_idx++)
|
||||
@@ -56,7 +55,14 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline, 1);
|
||||
|
||||
wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
|
||||
if (slicerList.empty()) //Wait, there is nothing to slice.
|
||||
{
|
||||
wireFrame.z_bottom = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
|
||||
}
|
||||
|
||||
Point starting_point_in_layer;
|
||||
if (wireFrame.bottom_outline.size() > 0)
|
||||
@@ -136,16 +142,21 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
|
||||
|
||||
{ // roofs:
|
||||
|
||||
WeaveLayer& top_layer = wireFrame.layers.back();
|
||||
Polygons to_be_supported; // empty for the top layer
|
||||
fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
|
||||
if (!wireFrame.layers.empty()) //If there are no layers, create no roof.
|
||||
{
|
||||
WeaveLayer& top_layer = wireFrame.layers.back();
|
||||
Polygons to_be_supported; // empty for the top layer
|
||||
fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{ // bottom:
|
||||
Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
|
||||
fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
|
||||
if (!wireFrame.layers.empty()) //If there are no layers, create no bottom.
|
||||
{
|
||||
Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
|
||||
fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@
|
||||
#include "MeshGroup.h"
|
||||
#include "slicer.h"
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
|
||||
@@ -19,7 +20,7 @@ namespace cura
|
||||
/*!
|
||||
* The main weaver / WirePrint / wireframe printing class, which computes the basic paths to be followed.
|
||||
*/
|
||||
class Weaver : public SettingsMessenger
|
||||
class Weaver : public SettingsMessenger, NoCopy
|
||||
{
|
||||
friend class Wireframe2gcode;
|
||||
private:
|
||||
|
||||
+47
-20
@@ -22,12 +22,20 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
|
||||
processStartingCode(commandSocket);
|
||||
|
||||
int maxObjectHeight = wireFrame.layers.back().z1;
|
||||
int maxObjectHeight;
|
||||
if (wireFrame.layers.empty())
|
||||
{
|
||||
maxObjectHeight = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxObjectHeight = wireFrame.layers.back().z1;
|
||||
}
|
||||
|
||||
processSkirt(commandSocket);
|
||||
|
||||
|
||||
unsigned int totalLayers = wireFrame.layers.size();
|
||||
unsigned int total_layers = wireFrame.layers.size();
|
||||
gcode.writeLayerComment(0);
|
||||
gcode.writeTypeComment("SKIRT");
|
||||
|
||||
@@ -71,7 +79,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
|
||||
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, totalLayers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_nr];
|
||||
|
||||
@@ -157,7 +165,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
|
||||
gcode.writeFanCommand(0);
|
||||
|
||||
finalize(maxObjectHeight);
|
||||
finalize();
|
||||
|
||||
if (commandSocket)
|
||||
{
|
||||
@@ -231,11 +239,14 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
|
||||
|
||||
RetractionConfig retraction_config;
|
||||
// TODO: get these from the settings!
|
||||
retraction_config.amount = 500; //INT2MM(getSettingInt("retraction_amount"))
|
||||
retraction_config.primeAmount = 0;//INT2MM(getSettingInt("retractionPrime
|
||||
retraction_config.distance = 500; //INT2MM(getSettingInt("retraction_amount"))
|
||||
retraction_config.prime_volume = 0;//INT2MM(getSettingInt("retractionPrime
|
||||
retraction_config.speed = 20; // 40;
|
||||
retraction_config.primeSpeed = 15; // 30;
|
||||
retraction_config.zHop = 0; //getSettingInt("retraction_hop");
|
||||
retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
|
||||
retraction_config.retraction_extrusion_window = INT2MM(getSettingInMicrons("retraction_extrusion_window"));
|
||||
retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
|
||||
|
||||
double top_retract_pause = 2.0;
|
||||
int retract_hop_dist = 1000;
|
||||
@@ -473,8 +484,8 @@ void Wireframe2gcode::writeMoveWithRetract(Point to)
|
||||
Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base)
|
||||
: SettingsMessenger(settings_base)
|
||||
, gcode(gcode)
|
||||
, wireFrame(weaver.wireFrame)
|
||||
{
|
||||
wireFrame = weaver.wireFrame;
|
||||
initial_layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
connectionHeight = getSettingInMicrons("wireframe_height");
|
||||
roof_inset = getSettingInMicrons("wireframe_roof_inset");
|
||||
@@ -529,13 +540,14 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBas
|
||||
roof_outer_delay = getSettingInSeconds("wireframe_roof_outer_delay");
|
||||
|
||||
|
||||
standard_retraction_config.amount = INT2MM(getSettingInMicrons("retraction_amount"));
|
||||
standard_retraction_config.primeAmount = INT2MM(getSettingInMicrons("retraction_extra_prime_amount"));
|
||||
standard_retraction_config.distance = INT2MM(getSettingInMicrons("retraction_amount"));
|
||||
standard_retraction_config.prime_volume = getSettingInCubicMillimeters("retraction_extra_prime_amount");
|
||||
standard_retraction_config.speed = getSettingInMillimetersPerSecond("retraction_retract_speed");
|
||||
standard_retraction_config.primeSpeed = getSettingInMillimetersPerSecond("retraction_prime_speed");
|
||||
standard_retraction_config.zHop = getSettingInMicrons("retraction_hop");
|
||||
|
||||
|
||||
standard_retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
|
||||
standard_retraction_config.retraction_extrusion_window = INT2MM(getSettingInMicrons("retraction_extrusion_window"));
|
||||
standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
|
||||
}
|
||||
|
||||
void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
|
||||
@@ -549,13 +561,24 @@ void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
|
||||
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), true);
|
||||
|
||||
if (getSettingInDegreeCelsius("material_print_temperature") > 0)
|
||||
if (getSettingBoolean("material_bed_temp_prepend"))
|
||||
{
|
||||
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"));
|
||||
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"), true);
|
||||
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
|
||||
{
|
||||
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), getSettingBoolean("material_bed_temp_wait"));
|
||||
}
|
||||
}
|
||||
|
||||
if (getSettingBoolean("material_print_temp_prepend"))
|
||||
{
|
||||
if (getSettingInDegreeCelsius("material_print_temperature") > 0)
|
||||
{
|
||||
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"));
|
||||
if (getSettingBoolean("machine_print_temp_wait"))
|
||||
{
|
||||
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -574,8 +597,12 @@ void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
|
||||
|
||||
void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
|
||||
{
|
||||
if (wireFrame.bottom_outline.size() == 0) //If we have no layers, don't create a skirt either.
|
||||
{
|
||||
return;
|
||||
}
|
||||
Polygons skirt = wireFrame.bottom_outline.offset(100000+5000).offset(-100000);
|
||||
PathOrderOptimizer order(gcode.getStartPositionXY());
|
||||
PathOrderOptimizer order(Point(INT32_MIN, INT32_MIN));
|
||||
order.addPolygons(skirt);
|
||||
order.optimize();
|
||||
|
||||
@@ -593,9 +620,9 @@ void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
|
||||
}
|
||||
|
||||
|
||||
void Wireframe2gcode::finalize(int maxObjectHeight)
|
||||
void Wireframe2gcode::finalize()
|
||||
{
|
||||
gcode.finalize(maxObjectHeight, getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
|
||||
gcode.writeTemperatureCommand(e, 0, false);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <functional> // passing function pointer or lambda as argument to a function
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
|
||||
#include "weaveDataStorage.h"
|
||||
#include "commandSocket.h"
|
||||
#include "settings.h"
|
||||
@@ -22,7 +24,7 @@ namespace cura
|
||||
/*!
|
||||
* Export class for exporting wireframe print gcode / weaver gcode / wireprint gcode.
|
||||
*/
|
||||
class Wireframe2gcode : public SettingsMessenger
|
||||
class Wireframe2gcode : public SettingsMessenger, NoCopy
|
||||
{
|
||||
private:
|
||||
static const int STRATEGY_COMPENSATE = 0;
|
||||
@@ -73,7 +75,7 @@ public:
|
||||
|
||||
|
||||
private:
|
||||
WireFrame wireFrame;
|
||||
WireFrame& wireFrame;
|
||||
|
||||
/*!
|
||||
* Startup gcode: nozzle temp up, retraction settings, bed temp
|
||||
@@ -88,7 +90,7 @@ private:
|
||||
/*!
|
||||
* End gcode: nozzle temp down
|
||||
*/
|
||||
void finalize(int maxObjectHeight);
|
||||
void finalize();
|
||||
|
||||
void writeFill(std::vector<WeaveRoofPart>& infill_insets, Polygons& outlines
|
||||
, std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)> connectionHandler
|
||||
|
||||
+74
-68
@@ -5,34 +5,11 @@
|
||||
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "utils/SVG.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
bool Comb::moveInsideBoundary(Point* p, int distance)
|
||||
{
|
||||
return PolygonUtils::moveInside(boundary_inside, *p, distance) != NO_INDEX;
|
||||
}
|
||||
|
||||
Polygons Comb::getLayerSecondWalls()
|
||||
{
|
||||
Polygons layer_walls;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
for (SliceLayerPart& part : mesh.layers[layer_nr].parts)
|
||||
{
|
||||
if (part.insets.size() >= 2)
|
||||
{
|
||||
layer_walls.add(part.insets[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
layer_walls.add(part.outline.offset(-offset_from_outlines));
|
||||
}
|
||||
}
|
||||
}
|
||||
return layer_walls;
|
||||
}
|
||||
|
||||
// boundary_outside is only computed when it's needed!
|
||||
Polygons* Comb::getBoundaryOutside()
|
||||
{
|
||||
@@ -44,7 +21,7 @@ Polygons* Comb::getBoundaryOutside()
|
||||
return boundary_outside;
|
||||
}
|
||||
|
||||
Comb::Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
: storage(storage)
|
||||
, layer_nr(layer_nr)
|
||||
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
|
||||
@@ -52,7 +29,7 @@ Comb::Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t comb_bounda
|
||||
, offset_from_outlines_outside(travel_avoid_distance)
|
||||
, avoid_other_parts(travel_avoid_other_parts)
|
||||
// , boundary_inside( boundary.offset(-offset_from_outlines) ) // TODO: make inside boundary configurable?
|
||||
, boundary_inside( getLayerSecondWalls() )
|
||||
, boundary_inside( comb_boundary_inside )
|
||||
, boundary_outside(nullptr)
|
||||
, partsView_inside( boundary_inside.splitIntoPartsView() ) // !! changes the order of boundary_inside !!
|
||||
{
|
||||
@@ -64,7 +41,7 @@ Comb::~Comb()
|
||||
delete boundary_outside;
|
||||
}
|
||||
|
||||
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside)
|
||||
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside, int64_t max_comb_distance_ignored)
|
||||
{
|
||||
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored))
|
||||
{
|
||||
@@ -78,7 +55,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
if (startInside)
|
||||
{
|
||||
start_inside_poly = PolygonUtils::moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2);
|
||||
if (!inside(start_inside_poly) || start_inside_poly == NO_INDEX)
|
||||
if (!boundary_inside.inside(start_inside_poly) || start_inside_poly == NO_INDEX)
|
||||
{
|
||||
if (start_inside_poly != NO_INDEX)
|
||||
{ // if not yet inside because of overshoot, try again
|
||||
@@ -94,7 +71,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
if (endInside)
|
||||
{
|
||||
end_inside_poly = PolygonUtils::moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2);
|
||||
if (!inside(endPoint) || end_inside_poly == NO_INDEX)
|
||||
if (!boundary_inside.inside(endPoint) || end_inside_poly == NO_INDEX)
|
||||
{
|
||||
if (end_inside_poly != NO_INDEX)
|
||||
{ // if not yet inside because of overshoot, try again
|
||||
@@ -117,13 +94,15 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
{ // normal combing within part
|
||||
PolygonsPart part = partsView_inside.assemblePart(start_part_idx);
|
||||
combPaths.emplace_back();
|
||||
LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
|
||||
LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{ // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed)
|
||||
Point middle_from;
|
||||
Point middle_to;
|
||||
Point inside_middle_from;
|
||||
Point inside_middle_to;
|
||||
|
||||
if (startInside && endInside)
|
||||
{
|
||||
@@ -131,27 +110,36 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from_cp.location, boundary_inside[end_part_boundary_poly_idx]);
|
||||
// walkToNearestSmallestConnection(middle_from_cp, middle_to_cp); // TODO: perform this optimization?
|
||||
middle_from = middle_from_cp.location;
|
||||
inside_middle_from = middle_from_cp.location;
|
||||
middle_to = middle_to_cp.location;
|
||||
inside_middle_to = middle_to_cp.location;
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored); //Also move the intermediary waypoint inside if it isn't yet.
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
|
||||
}
|
||||
else
|
||||
else if(!startInside && !endInside)
|
||||
{
|
||||
if (!startInside && !endInside)
|
||||
{
|
||||
middle_from = startPoint;
|
||||
middle_to = endPoint;
|
||||
}
|
||||
else if (!startInside && endInside)
|
||||
{
|
||||
middle_from = startPoint;
|
||||
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from, boundary_inside[end_part_boundary_poly_idx]);
|
||||
middle_to = middle_to_cp.location;
|
||||
}
|
||||
else if (startInside && !endInside)
|
||||
{
|
||||
middle_to = endPoint;
|
||||
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to, boundary_inside[start_part_boundary_poly_idx]);
|
||||
middle_from = middle_from_cp.location;
|
||||
}
|
||||
middle_from = startPoint;
|
||||
inside_middle_from = startPoint;
|
||||
middle_to = endPoint;
|
||||
inside_middle_to = endPoint;
|
||||
}
|
||||
else if(!startInside && endInside)
|
||||
{
|
||||
middle_from = startPoint;
|
||||
inside_middle_from = startPoint;
|
||||
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from,boundary_inside[end_part_boundary_poly_idx]);
|
||||
middle_to = middle_to_cp.location;
|
||||
inside_middle_to = middle_to_cp.location;
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
|
||||
}
|
||||
else if(startInside && !endInside)
|
||||
{
|
||||
middle_to = endPoint;
|
||||
inside_middle_to = endPoint;
|
||||
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to,boundary_inside[start_part_boundary_poly_idx]);
|
||||
middle_from = middle_from_cp.location;
|
||||
inside_middle_from = middle_from_cp.location;
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
|
||||
}
|
||||
|
||||
if (startInside)
|
||||
@@ -159,7 +147,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
// start to boundary
|
||||
PolygonsPart part_begin = partsView_inside.assemblePart(start_part_idx); // comb through the starting part only
|
||||
combPaths.emplace_back();
|
||||
LinePolygonsCrossings::comb(part_begin, startPoint, middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
|
||||
LinePolygonsCrossings::comb(part_begin, startPoint, inside_middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
}
|
||||
|
||||
// throught air from boundary to boundary
|
||||
@@ -178,14 +166,14 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
}
|
||||
combPaths.emplace_back();
|
||||
combPaths.back().throughAir = true;
|
||||
if ( vSize(middle_from - middle_to) < vSize(middle_from - from_outside) + vSize(middle_to - to_outside) )
|
||||
if ( vSize(inside_middle_from - inside_middle_to) < vSize(inside_middle_from - from_outside) + vSize(inside_middle_to - to_outside) )
|
||||
{ // via outside is a detour
|
||||
combPaths.back().push_back(middle_from);
|
||||
combPaths.back().push_back(middle_to);
|
||||
combPaths.back().push_back(inside_middle_from);
|
||||
combPaths.back().push_back(inside_middle_to);
|
||||
}
|
||||
else
|
||||
{
|
||||
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside);
|
||||
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -193,8 +181,8 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
combPaths.emplace_back();
|
||||
combPaths.back().throughAir = true;
|
||||
combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary!
|
||||
combPaths.back().push_back(middle_from);
|
||||
combPaths.back().push_back(middle_to);
|
||||
combPaths.back().push_back(inside_middle_from);
|
||||
combPaths.back().push_back(inside_middle_to);
|
||||
}
|
||||
|
||||
if (endInside)
|
||||
@@ -202,7 +190,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
// boundary to end
|
||||
PolygonsPart part_end = partsView_inside.assemblePart(end_part_idx); // comb through end part only
|
||||
combPaths.emplace_back();
|
||||
LinePolygonsCrossings::comb(part_end, middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
|
||||
LinePolygonsCrossings::comb(part_end, inside_middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -219,18 +207,32 @@ void LinePolygonsCrossings::calcScanlineCrossings()
|
||||
{
|
||||
PolyCrossings minMax(poly_idx);
|
||||
PolygonRef poly = boundary[poly_idx];
|
||||
Point p0 = transformation_matrix.apply(poly.back());
|
||||
Point p0 = transformation_matrix.apply(poly[poly.size() - 1]);
|
||||
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
Point p1 = transformation_matrix.apply(poly[point_idx]);
|
||||
if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y))
|
||||
if((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y))
|
||||
{
|
||||
if(p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line.
|
||||
{
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
|
||||
|
||||
if (x >= transformed_startPoint.X && x <= transformed_endPoint.X)
|
||||
{
|
||||
if (x < minMax.min.x) { minMax.min.x = x; minMax.min.point_idx = point_idx; }
|
||||
if (x > minMax.max.x) { minMax.max.x = x; minMax.max.point_idx = point_idx; }
|
||||
if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border.
|
||||
//Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline.
|
||||
{
|
||||
minMax.min.x = x;
|
||||
minMax.min.point_idx = point_idx;
|
||||
}
|
||||
if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border.
|
||||
{
|
||||
minMax.max.x = x;
|
||||
minMax.max.point_idx = point_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
p0 = p1;
|
||||
@@ -276,14 +278,14 @@ bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
|
||||
}
|
||||
|
||||
|
||||
void LinePolygonsCrossings::getCombingPath(CombPath& combPath)
|
||||
void LinePolygonsCrossings::getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored)
|
||||
{
|
||||
if (shorterThen(endPoint - startPoint, Comb::max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
|
||||
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
|
||||
{
|
||||
//We're not crossing any boundaries. So skip the comb generation.
|
||||
combPath.push_back(startPoint);
|
||||
combPath.push_back(endPoint);
|
||||
return;
|
||||
combPath.push_back(endPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
calcScanlineCrossings();
|
||||
@@ -308,7 +310,7 @@ void LinePolygonsCrossings::getBasicCombingPath(CombPath& combPath)
|
||||
void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
|
||||
{
|
||||
PolygonRef poly = boundary[polyCrossings.poly_idx];
|
||||
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x, transformed_startPoint.Y)));
|
||||
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x + dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
|
||||
if ( ( polyCrossings.max.point_idx - polyCrossings.min.point_idx + poly.size() ) % poly.size()
|
||||
< poly.size() / 2 )
|
||||
{ // follow the path in the same direction as the winding order of the boundary polygon
|
||||
@@ -323,13 +325,13 @@ void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, Co
|
||||
{ // follow the path in the opposite direction of the winding order of the boundary polygon
|
||||
unsigned int min_idx = (polyCrossings.min.point_idx == 0)? poly.size() - 1: polyCrossings.min.point_idx - 1;
|
||||
unsigned int max_idx = (polyCrossings.max.point_idx == 0)? poly.size() - 1: polyCrossings.max.point_idx - 1;
|
||||
|
||||
|
||||
for(unsigned int point_idx = min_idx; point_idx != max_idx; point_idx = (point_idx > 0) ? (point_idx - 1) : (poly.size() - 1))
|
||||
{
|
||||
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
|
||||
}
|
||||
}
|
||||
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x, transformed_startPoint.Y)));
|
||||
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x - dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
|
||||
}
|
||||
|
||||
|
||||
@@ -348,10 +350,14 @@ LinePolygonsCrossings::PolyCrossings* LinePolygonsCrossings::getNextPolygonAlong
|
||||
}
|
||||
|
||||
bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimized_comb_path)
|
||||
{
|
||||
{
|
||||
optimized_comb_path.push_back(startPoint);
|
||||
for(unsigned int point_idx = 1; point_idx<comb_path.size(); point_idx++)
|
||||
{
|
||||
if(comb_path[point_idx] == comb_path[point_idx - 1]) //Two points are the same. Skip the second.
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Point& current_point = optimized_comb_path.back();
|
||||
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx]))
|
||||
{
|
||||
|
||||
+8
-26
@@ -113,7 +113,7 @@ private:
|
||||
*
|
||||
* \param combPath Output parameter: the points along the combing path.
|
||||
*/
|
||||
void getCombingPath(CombPath& combPath);
|
||||
void getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored = MM2INT(1.5));
|
||||
|
||||
/*!
|
||||
* Get the basic combing path, without shortcuts. The path goes straight toward the endPoint and follows the boundary when it hits it, until it passes the scanline again.
|
||||
@@ -178,10 +178,10 @@ public:
|
||||
* \param endPoint Where to end the combing move.
|
||||
* \param combPath Output parameter: the combing path generated.
|
||||
*/
|
||||
static void comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside)
|
||||
static void comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside, int64_t max_comb_distance_ignored = MM2INT(1.5))
|
||||
{
|
||||
LinePolygonsCrossings linePolygonsCrossings(boundary, startPoint, endPoint, dist_to_move_boundary_point_outside);
|
||||
linePolygonsCrossings.getCombingPath(combPath);
|
||||
linePolygonsCrossings.getCombingPath(combPath, max_comb_distance_ignored);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -205,26 +205,20 @@ class Comb
|
||||
friend class LinePolygonsCrossings;
|
||||
private:
|
||||
SliceDataStorage& storage; //!< The storage from which to compute the outside boundary, when needed.
|
||||
unsigned int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
|
||||
int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
|
||||
|
||||
int64_t offset_from_outlines; //!< Offset from the boundary of a part to the comb path. (nozzle width / 2)
|
||||
int64_t max_moveInside_distance2; //!< Maximal distance of a point to the Comb::boundary_inside which is still to be considered inside. (very sharp corners not allowed :S)
|
||||
int64_t offset_from_outlines_outside; //!< Offset from the boundary of a part to a travel path which avoids it by this distance.
|
||||
static const int64_t max_moveOutside_distance2 = INT64_MAX; //!< Any point which is not inside should be considered outside.
|
||||
static const int64_t offset_dist_to_get_from_on_the_polygon_to_outside = 40; //!< in order to prevent on-boundary vs crossing boundary confusions (precision thing)
|
||||
static const int64_t max_comb_distance_ignored = MM2INT(1.5); //!< If the direct path from start point to end point is shorter than this, go directly without any combing.
|
||||
static const int64_t offset_extra_start_end = 100; //!< Distance to move start point and end point toward eachother to extra avoid collision with the boundaries.
|
||||
|
||||
bool avoid_other_parts; //!< Whether to perform inverse combing a.k.a. avoid parts.
|
||||
|
||||
Polygons boundary_inside; //!< The boundary within which to comb.
|
||||
Polygons& boundary_inside; //!< The boundary within which to comb.
|
||||
Polygons* boundary_outside; //!< The boundary outside of which to stay to avoid collision with other layer parts. This is a pointer cause we only compute it when we move outside the boundary (so not when there is only a single part in the layer)
|
||||
PartsView partsView_inside; //!< Structured indices onto boundary_inside which shows which polygons belong to which part.
|
||||
|
||||
/*!
|
||||
* Collects the inner most walls for every mesh in the layer (not support) or computes them from the outlines using Comb::offset_from_outlines.
|
||||
*/
|
||||
Polygons getLayerSecondWalls();
|
||||
|
||||
/*!
|
||||
* Get the boundary_outside, which is an offset from the outlines of all meshes in the layer. Calculate it when it hasn't been calculated yet.
|
||||
@@ -236,16 +230,14 @@ public:
|
||||
* Initializes the combing areas for every mesh in the layer (not support)
|
||||
* \param storage Where the layer polygon data is stored
|
||||
* \param layer_nr The number of the layer for which to generate the combing areas.
|
||||
* \param comb_boundary_inside The comb boundary within which to comb within layer parts.
|
||||
* \param offset_from_outlines The offset from the outline polygon, to create the combing boundary in case there is no second wall.
|
||||
* \param travel_avoid_other_parts Whether to avoid other layer parts when traveling through air.
|
||||
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
|
||||
*/
|
||||
Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
|
||||
~Comb();
|
||||
|
||||
//! Utility function for `boundary_inside.inside(p)`.
|
||||
bool inside(const Point p) { return boundary_inside.inside(p); }
|
||||
|
||||
/*!
|
||||
* Calculate the comb paths (if any) - one for each polygon combed alternated with travel paths
|
||||
@@ -257,17 +249,7 @@ public:
|
||||
* \param endInside Whether we want to end up inside the comb boundary
|
||||
* \return Whether combing has succeeded; otherwise a retraction is needed.
|
||||
*/
|
||||
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false);
|
||||
|
||||
/*!
|
||||
* Move \p p to inside the inner comb boundary with a \p distance from the boundary.
|
||||
*
|
||||
* \param p the point to change/move
|
||||
* \param distance the distance from the resulting point to the boundary on the inside
|
||||
* \return whether the point has been moved inside
|
||||
*/
|
||||
bool moveInsideBoundary(Point* p, int distance);
|
||||
|
||||
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false, int64_t max_comb_distance_ignored = MM2INT(1.5));
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+102
-13
@@ -6,18 +6,26 @@
|
||||
#include <thread>
|
||||
#include <cinttypes>
|
||||
|
||||
#ifdef ARCUS
|
||||
#include <Arcus/Socket.h>
|
||||
#endif
|
||||
|
||||
#include <string> // stoi
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#define DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(x)
|
||||
// std::cerr << x;
|
||||
|
||||
namespace cura {
|
||||
|
||||
#define BYTES_PER_FLOAT 4
|
||||
#define FLOATS_PER_VECTOR 3
|
||||
#define VECTORS_PER_FACE 3
|
||||
|
||||
|
||||
#ifdef ARCUS
|
||||
class CommandSocket::Private
|
||||
{
|
||||
public:
|
||||
@@ -26,6 +34,8 @@ public:
|
||||
, object_count(0)
|
||||
, current_sliced_object(nullptr)
|
||||
, sliced_objects(0)
|
||||
, current_layer_count(0)
|
||||
, current_layer_offset(0)
|
||||
{ }
|
||||
|
||||
cura::proto::Layer* getLayerById(int id);
|
||||
@@ -43,6 +53,11 @@ public:
|
||||
|
||||
// Number of sliced objects for this sliced object list
|
||||
int sliced_objects;
|
||||
|
||||
// Number of layers sent to the front end so far
|
||||
// Used for incrementing the current layer in one at a time mode
|
||||
int current_layer_count;
|
||||
int current_layer_offset;
|
||||
|
||||
// Ids of the sliced objects
|
||||
std::vector<int64_t> object_ids;
|
||||
@@ -53,15 +68,21 @@ public:
|
||||
// Print object that olds one or more meshes that need to be sliced.
|
||||
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
|
||||
};
|
||||
#endif
|
||||
|
||||
CommandSocket::CommandSocket()
|
||||
#ifdef ARCUS
|
||||
: d(new Private)
|
||||
#endif
|
||||
{
|
||||
#ifdef ARCUS
|
||||
FffProcessor::getInstance()->setCommandSocket(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::connect(const std::string& ip, int port)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
d->socket = new Arcus::Socket();
|
||||
//d->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
|
||||
d->socket->registerMessageType(1, &cura::proto::Slice::default_instance());
|
||||
@@ -74,18 +95,27 @@ void CommandSocket::connect(const std::string& ip, int port)
|
||||
|
||||
d->socket->connect(ip, port);
|
||||
|
||||
bool slice_another_time = true;
|
||||
|
||||
// Start & continue listening as long as socket is not closed and there is no error.
|
||||
while(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error)
|
||||
while(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error && slice_another_time)
|
||||
{
|
||||
//If there is an object to slice, do so.
|
||||
if(d->objects_to_slice.size())
|
||||
{
|
||||
FffProcessor::getInstance()->resetFileNumber();
|
||||
for(auto object : d->objects_to_slice)
|
||||
{
|
||||
FffProcessor::getInstance()->processMeshGroup(object.get());
|
||||
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
|
||||
{
|
||||
logError("Slicing mesh group failed!");
|
||||
}
|
||||
}
|
||||
d->objects_to_slice.clear();
|
||||
FffProcessor::getInstance()->finalize();
|
||||
sendGCodeLayer();
|
||||
sendPrintTime();
|
||||
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
|
||||
//TODO: Support all-at-once/one-at-a-time printing
|
||||
//d->processor->processModel(d->object_to_slice.get());
|
||||
//d->object_to_slice.reset();
|
||||
@@ -128,19 +158,44 @@ void CommandSocket::connect(const std::string& ip, int port)
|
||||
d->socket->clearError();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ARCUS
|
||||
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
{
|
||||
FMatrix3x3 matrix;
|
||||
//d->object_count = 0;
|
||||
//d->object_ids.clear();
|
||||
d->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
|
||||
MeshGroup* object_to_slice = d->objects_to_slice.back().get();
|
||||
MeshGroup* meshgroup = d->objects_to_slice.back().get();
|
||||
|
||||
for(auto setting : list->settings())
|
||||
{
|
||||
meshgroup->setSetting(setting.name(), setting.value());
|
||||
}
|
||||
|
||||
for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++)
|
||||
{ // initialize remaining extruder trains and load the defaults
|
||||
meshgroup->createExtruderTrain(extruder_nr)->setExtruderTrainDefaults(extruder_nr); // create new extruder train objects or use already existing ones
|
||||
}
|
||||
|
||||
for(auto object : list->objects())
|
||||
{
|
||||
object_to_slice->meshes.push_back(object_to_slice); //Construct a new mesh (with object_to_slice as settings parent object) and put it into MeshGroup's mesh list.
|
||||
Mesh& mesh = object_to_slice->meshes.back();
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("solid Cura_out\n");
|
||||
int extruder_train_nr = 0; // TODO: make primary extruder configurable!
|
||||
for(auto setting : object.settings())
|
||||
{
|
||||
if (setting.name() == "extruder_nr")
|
||||
{
|
||||
extruder_train_nr = std::stoi(setting.value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
SettingsBase* extruder_train = meshgroup->getExtruderTrain(extruder_train_nr);
|
||||
|
||||
meshgroup->meshes.push_back(extruder_train); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
|
||||
Mesh& mesh = meshgroup->meshes.back();
|
||||
|
||||
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
|
||||
int face_count = object.vertices().size() / bytes_per_face;
|
||||
@@ -155,8 +210,16 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
verts[1] = matrix.apply(float_vertices[1]);
|
||||
verts[2] = matrix.apply(float_vertices[2]);
|
||||
mesh.addFace(verts[0], verts[1], verts[2]);
|
||||
}
|
||||
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" facet normal -1 0 0\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" outer loop\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[0].x) <<" " << INT2MM(verts[0].y) <<" " << INT2MM(verts[0].z) << "\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[1].x) <<" " << INT2MM(verts[1].y) <<" " << INT2MM(verts[1].z) << "\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[2].x) <<" " << INT2MM(verts[2].y) <<" " << INT2MM(verts[2].z) << "\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endloop\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endfacet\n");
|
||||
}
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("endsolid Cura_out\n");
|
||||
for(auto setting : object.settings())
|
||||
{
|
||||
mesh.setSetting(setting.name(), setting.value());
|
||||
@@ -166,13 +229,8 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
mesh.finish();
|
||||
}
|
||||
|
||||
for(auto setting : list->settings())
|
||||
{
|
||||
object_to_slice->setSetting(setting.name(), setting.value());
|
||||
}
|
||||
|
||||
d->object_count++;
|
||||
object_to_slice->finalize();
|
||||
meshgroup->finalize();
|
||||
}
|
||||
|
||||
void CommandSocket::handleSettingList(cura::proto::SettingList* list)
|
||||
@@ -182,9 +240,11 @@ void CommandSocket::handleSettingList(cura::proto::SettingList* list)
|
||||
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!d->current_sliced_object)
|
||||
{
|
||||
return;
|
||||
@@ -193,10 +253,12 @@ void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
|
||||
cura::proto::Layer* layer = d->getLayerById(layer_nr);
|
||||
layer->set_height(z);
|
||||
layer->set_thickness(height);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!d->current_sliced_object)
|
||||
return;
|
||||
|
||||
@@ -214,15 +276,18 @@ void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polyg
|
||||
p->set_points(polydata);
|
||||
p->set_line_width(line_width);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendProgress(float amount)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::Progress>();
|
||||
amount /= d->object_count;
|
||||
amount += d->sliced_objects * (1. / d->object_count);
|
||||
message->set_amount(amount);
|
||||
d->socket->sendMessage(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendProgressStage(Progress::Stage stage)
|
||||
@@ -232,10 +297,12 @@ void CommandSocket::sendProgressStage(Progress::Stage stage)
|
||||
|
||||
void CommandSocket::sendPrintTime()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::ObjectPrintTime>();
|
||||
message->set_time(FffProcessor::getInstance()->getTotalPrintTime());
|
||||
message->set_material_amount(FffProcessor::getInstance()->getTotalFilamentUsed(0));
|
||||
d->socket->sendMessage(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
|
||||
@@ -249,6 +316,7 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
|
||||
|
||||
void CommandSocket::beginSendSlicedObject()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!d->sliced_object_list)
|
||||
{
|
||||
d->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
|
||||
@@ -256,45 +324,64 @@ void CommandSocket::beginSendSlicedObject()
|
||||
|
||||
d->current_sliced_object = d->sliced_object_list->add_objects();
|
||||
d->current_sliced_object->set_id(d->object_ids[d->sliced_objects]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::endSendSlicedObject()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
d->sliced_objects++;
|
||||
d->current_layer_offset = d->current_layer_count;
|
||||
std::cout << "End sliced object called. sliced objects " << d->sliced_objects << " object count: " << d->object_count << std::endl;
|
||||
|
||||
std::cout << "current layer count" << d->current_layer_count << std::endl;
|
||||
std::cout << "current layer offset" << d->current_layer_offset << std::endl;
|
||||
|
||||
if(d->sliced_objects >= d->object_count)
|
||||
{
|
||||
d->socket->sendMessage(d->sliced_object_list);
|
||||
d->sliced_objects = 0;
|
||||
d->current_layer_count = 0;
|
||||
d->current_layer_offset = 0;
|
||||
d->sliced_object_list.reset();
|
||||
d->current_sliced_object = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::beginGCode()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
FffProcessor::getInstance()->setTargetStream(&d->gcode_output_stream);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendGCodeLayer()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::GCodeLayer>();
|
||||
message->set_id(d->object_ids[0]);
|
||||
message->set_data(d->gcode_output_stream.str());
|
||||
d->socket->sendMessage(message);
|
||||
|
||||
d->gcode_output_stream.str("");
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendGCodePrefix(std::string prefix)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::GCodePrefix>();
|
||||
message->set_data(prefix);
|
||||
d->socket->sendMessage(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ARCUS
|
||||
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
|
||||
{
|
||||
id += current_layer_offset;
|
||||
|
||||
auto itr = std::find_if(current_sliced_object->mutable_layers()->begin(), current_sliced_object->mutable_layers()->end(), [id](cura::proto::Layer& l) { return l.id() == id; });
|
||||
|
||||
cura::proto::Layer* layer = nullptr;
|
||||
@@ -306,9 +393,11 @@ cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
|
||||
{
|
||||
layer = current_sliced_object->add_layers();
|
||||
layer->set_id(id);
|
||||
current_layer_count++;
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
#endif
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef ARCUS
|
||||
#include "Cura.pb.h"
|
||||
#endif
|
||||
|
||||
namespace cura {
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class CommandSocket
|
||||
{
|
||||
@@ -24,7 +26,8 @@ public:
|
||||
* \param port int of the port to connect with.
|
||||
*/
|
||||
void connect(const std::string& ip, int port);
|
||||
|
||||
|
||||
#ifdef ARCUS
|
||||
/*!
|
||||
* Handler for ObjectList message.
|
||||
* Loads all objects from the message and starts the slicing process
|
||||
@@ -36,6 +39,7 @@ public:
|
||||
* This simply sets all the settings by using key value pair
|
||||
*/
|
||||
void handleSettingList(cura::proto::SettingList* list);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Does nothing at the moment
|
||||
@@ -74,9 +78,11 @@ public:
|
||||
void sendGCodeLayer();
|
||||
void sendGCodePrefix(std::string prefix);
|
||||
|
||||
#ifdef ARCUS
|
||||
private:
|
||||
class Private;
|
||||
const std::unique_ptr<Private> d;
|
||||
#endif
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+301
-205
@@ -1,6 +1,7 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <stdarg.h>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
|
||||
#include "gcodeExport.h"
|
||||
#include "utils/logoutput.h"
|
||||
@@ -8,19 +9,19 @@
|
||||
namespace cura {
|
||||
|
||||
GCodeExport::GCodeExport()
|
||||
: output_stream(&std::cout), currentPosition(0,0,0), startPosition(INT32_MIN,INT32_MIN,0)
|
||||
: output_stream(&std::cout)
|
||||
, currentPosition(0,0,MM2INT(20))
|
||||
, commandSocket(nullptr)
|
||||
, layer_nr(0)
|
||||
{
|
||||
extrusion_amount = 0;
|
||||
current_e_value = 0;
|
||||
current_extruder = 0;
|
||||
currentFanSpeed = -1;
|
||||
|
||||
totalPrintTime = 0.0;
|
||||
|
||||
currentSpeed = 1;
|
||||
retractionPrimeSpeed = 1;
|
||||
isRetracted = false;
|
||||
isZHopped = false;
|
||||
last_coasted_amount_mm3 = 0;
|
||||
isZHopped = 0;
|
||||
setFlavor(EGCodeFlavor::REPRAP);
|
||||
}
|
||||
|
||||
@@ -28,6 +29,11 @@ GCodeExport::~GCodeExport()
|
||||
{
|
||||
}
|
||||
|
||||
void GCodeExport::setCommandSocketAndLayerNr(CommandSocket* commandSocket_, unsigned int layer_nr_) {
|
||||
commandSocket = commandSocket_;
|
||||
layer_nr = layer_nr_;
|
||||
}
|
||||
|
||||
void GCodeExport::setOutputStream(std::ostream* stream)
|
||||
{
|
||||
output_stream = stream;
|
||||
@@ -63,6 +69,15 @@ void GCodeExport::setFlavor(EGCodeFlavor flavor)
|
||||
{
|
||||
is_volumatric = false;
|
||||
}
|
||||
|
||||
if (flavor == EGCodeFlavor::BFB || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC || flavor == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
firmware_retract = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
firmware_retract = false;
|
||||
}
|
||||
}
|
||||
|
||||
EGCodeFlavor GCodeExport::getFlavor()
|
||||
@@ -89,17 +104,6 @@ int GCodeExport::getPositionZ()
|
||||
return currentPosition.z;
|
||||
}
|
||||
|
||||
void GCodeExport::resetStartPosition()
|
||||
{
|
||||
startPosition.x = INT32_MIN;
|
||||
startPosition.y = INT32_MIN;
|
||||
}
|
||||
|
||||
Point GCodeExport::getStartPositionXY()
|
||||
{
|
||||
return Point(startPosition.x, startPosition.y);
|
||||
}
|
||||
|
||||
int GCodeExport::getExtruderNr()
|
||||
{
|
||||
return current_extruder;
|
||||
@@ -112,20 +116,21 @@ void GCodeExport::setFilamentDiameter(unsigned int extruder, int diameter)
|
||||
extruder_attr[extruder].filament_area = area;
|
||||
}
|
||||
|
||||
double GCodeExport::getFilamentArea(unsigned int extruder)
|
||||
{
|
||||
return extruder_attr[extruder].filament_area;
|
||||
}
|
||||
|
||||
double GCodeExport::getExtrusionAmountMM3(unsigned int extruder)
|
||||
double GCodeExport::getCurrentExtrudedVolume()
|
||||
{
|
||||
double extrusion_amount = current_e_value;
|
||||
if (!firmware_retract)
|
||||
{ // no E values are changed to perform a retraction
|
||||
extrusion_amount -= extruder_attr[current_extruder].retraction_e_amount_at_e_start; // subtract the increment in E which was used for the first unretraction instead of extrusion
|
||||
extrusion_amount += extruder_attr[current_extruder].retraction_e_amount_current; // add the decrement in E which the filament is behind on extrusion due to the last retraction
|
||||
}
|
||||
if (is_volumatric)
|
||||
{
|
||||
return extrusion_amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
return extrusion_amount * getFilamentArea(extruder);
|
||||
return extrusion_amount * extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,14 +138,10 @@ double GCodeExport::getExtrusionAmountMM3(unsigned int extruder)
|
||||
double GCodeExport::getTotalFilamentUsed(int e)
|
||||
{
|
||||
if (e == current_extruder)
|
||||
return extruder_attr[e].totalFilament + getExtrusionAmountMM3(e);
|
||||
return extruder_attr[e].totalFilament + getCurrentExtrudedVolume();
|
||||
return extruder_attr[e].totalFilament;
|
||||
}
|
||||
|
||||
double GCodeExport::getTotalPrintTime(EPrintFeature print_feature)
|
||||
{
|
||||
return total_print_time_per_feature[(unsigned int)print_feature];
|
||||
}
|
||||
double GCodeExport::getTotalPrintTime()
|
||||
{
|
||||
return totalPrintTime;
|
||||
@@ -149,30 +150,34 @@ double GCodeExport::getTotalPrintTime()
|
||||
void GCodeExport::resetTotalPrintTimeAndFilament()
|
||||
{
|
||||
totalPrintTime = 0;
|
||||
for (unsigned int feat_idx = 0; feat_idx < (unsigned int)EPrintFeature::ENUM_COUNT; feat_idx++)
|
||||
{
|
||||
total_print_time_per_feature[feat_idx] = 0.0;
|
||||
}
|
||||
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
|
||||
{
|
||||
extruder_attr[e].totalFilament = 0.0;
|
||||
extruder_attr[e].currentTemperature = 0;
|
||||
}
|
||||
extrusion_amount = 0.0;
|
||||
current_e_value = 0.0;
|
||||
estimateCalculator.reset();
|
||||
}
|
||||
|
||||
void GCodeExport::updateTotalPrintTime(EPrintFeature print_feature)
|
||||
void GCodeExport::updateTotalPrintTime()
|
||||
{
|
||||
double time = estimateCalculator.calculate();
|
||||
totalPrintTime += time;
|
||||
total_print_time_per_feature[(unsigned int)print_feature] += time;
|
||||
totalPrintTime += estimateCalculator.calculate();
|
||||
estimateCalculator.reset();
|
||||
}
|
||||
|
||||
void GCodeExport::writeComment(std::string comment)
|
||||
{
|
||||
*output_stream << ";" << comment << "\n";
|
||||
*output_stream << ";";
|
||||
for (unsigned int i = 0; i < comment.length(); i++)
|
||||
{
|
||||
if (comment[i] == '\n')
|
||||
{
|
||||
*output_stream << "\\n";
|
||||
}else{
|
||||
*output_stream << comment[i];
|
||||
}
|
||||
}
|
||||
*output_stream << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeTypeComment(const char* type)
|
||||
@@ -184,6 +189,11 @@ void GCodeExport::writeLayerComment(int layer_nr)
|
||||
*output_stream << ";LAYER:" << layer_nr << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeLayerCountComment(int layer_count)
|
||||
{
|
||||
*output_stream << ";LAYER_COUNT:" << layer_count << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeLine(const char* line)
|
||||
{
|
||||
*output_stream << line << "\n";
|
||||
@@ -191,20 +201,24 @@ void GCodeExport::writeLine(const char* line)
|
||||
|
||||
void GCodeExport::resetExtrusionValue()
|
||||
{
|
||||
if (extrusion_amount != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
|
||||
if (current_e_value != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
|
||||
{
|
||||
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0\n";
|
||||
extruder_attr[current_extruder].totalFilament += getExtrusionAmountMM3(current_extruder);
|
||||
for (unsigned int i = 0; i < extrusion_amount_at_previous_n_retractions.size(); i++)
|
||||
extrusion_amount_at_previous_n_retractions[i] -= extrusion_amount;
|
||||
extrusion_amount = 0.0;
|
||||
double current_extruded_volume = getCurrentExtrudedVolume();
|
||||
extruder_attr[current_extruder].totalFilament += current_extruded_volume;
|
||||
for (double& extruded_volume_at_retraction : extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions)
|
||||
{ // update the extruded_volume_at_previous_n_retractions only of the current extruder, since other extruders don't extrude the current volume
|
||||
extruded_volume_at_retraction -= current_extruded_volume;
|
||||
}
|
||||
current_e_value = 0.0;
|
||||
extruder_attr[current_extruder].retraction_e_amount_at_e_start = extruder_attr[current_extruder].retraction_e_amount_current;
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeExport::writeDelay(double timeAmount)
|
||||
{
|
||||
*output_stream << "G4 P" << int(timeAmount * 1000) << "\n";
|
||||
totalPrintTime += timeAmount;
|
||||
estimateCalculator.addTime(timeAmount);
|
||||
}
|
||||
|
||||
void GCodeExport::writeMove(Point p, double speed, double extrusion_mm3_per_mm)
|
||||
@@ -217,223 +231,306 @@ void GCodeExport::writeMove(Point3 p, double speed, double extrusion_mm3_per_mm)
|
||||
writeMove(p.x, p.y, p.z, speed, extrusion_mm3_per_mm);
|
||||
}
|
||||
|
||||
void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
|
||||
{
|
||||
double extrusion_per_mm = extrusion_mm3_per_mm;
|
||||
if (!is_volumatric)
|
||||
{
|
||||
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
|
||||
Point gcode_pos = getGcodePos(x,y, current_extruder);
|
||||
|
||||
//For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values.
|
||||
float fspeed = speed * 60;
|
||||
float rpm = extrusion_per_mm * speed * 60;
|
||||
const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion.
|
||||
rpm /= mm_per_rpm;
|
||||
if (rpm > 0)
|
||||
{
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
{
|
||||
if (currentSpeed != double(rpm))
|
||||
{
|
||||
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
|
||||
//fprintf(f, "M108 S%0.1f\r\n", rpm);
|
||||
*output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n";
|
||||
currentSpeed = double(rpm);
|
||||
}
|
||||
//Add M101 or M201 to enable the proper extruder.
|
||||
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n";
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = 0.0;
|
||||
}
|
||||
//Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
|
||||
// (Trick copied from KISSlicer, thanks Jonathan)
|
||||
fspeed *= (rpm / (roundf(rpm * 100) / 100));
|
||||
|
||||
//Increase the extrusion amount to calculate the amount of filament used.
|
||||
Point3 diff = Point3(x,y,z) - getPosition();
|
||||
|
||||
current_e_value += extrusion_per_mm * diff.vSizeMM();
|
||||
}
|
||||
else
|
||||
{
|
||||
//If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
|
||||
if (!extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
{
|
||||
*output_stream << "M103\r\n";
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically
|
||||
}
|
||||
}
|
||||
*output_stream << std::setprecision(3) <<
|
||||
"G1 X" << INT2MM(gcode_pos.X) <<
|
||||
" Y" << INT2MM(gcode_pos.Y) <<
|
||||
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n";
|
||||
|
||||
currentPosition = Point3(x, y, z);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed);
|
||||
}
|
||||
|
||||
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
|
||||
{
|
||||
if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z)
|
||||
return;
|
||||
|
||||
assert(speed*60 < 10000 && speed*60 > 100); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
|
||||
|
||||
#ifdef ASSERT_INSANE_OUTPUT
|
||||
assert(speed < 200 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
|
||||
assert(currentPosition != no_point3);
|
||||
assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release)
|
||||
|
||||
#endif //ASSERT_INSANE_OUTPUT
|
||||
|
||||
if (extrusion_mm3_per_mm < 0)
|
||||
logWarning("Warning! Negative extrusion move!");
|
||||
|
||||
double extrusion_per_mm = extrusion_mm3_per_mm;
|
||||
if (!is_volumatric)
|
||||
{
|
||||
extrusion_per_mm = extrusion_mm3_per_mm / getFilamentArea(current_extruder);
|
||||
}
|
||||
|
||||
Point gcode_pos = getGcodePos(x,y, current_extruder);
|
||||
|
||||
if (flavor == EGCodeFlavor::BFB)
|
||||
{
|
||||
//For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values.
|
||||
float fspeed = speed * 60;
|
||||
float rpm = extrusion_per_mm * speed * 60;
|
||||
const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion.
|
||||
rpm /= mm_per_rpm;
|
||||
if (rpm > 0)
|
||||
{
|
||||
if (isRetracted)
|
||||
{
|
||||
if (currentSpeed != double(rpm))
|
||||
{
|
||||
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
|
||||
//fprintf(f, "M108 S%0.1f\r\n", rpm);
|
||||
*output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n";
|
||||
currentSpeed = double(rpm);
|
||||
}
|
||||
//Add M101 or M201 to enable the proper extruder.
|
||||
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n";
|
||||
isRetracted = false;
|
||||
}
|
||||
//Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
|
||||
// (Trick copied from KISSlicer, thanks Jonathan)
|
||||
fspeed *= (rpm / (roundf(rpm * 100) / 100));
|
||||
writeMoveBFB(x, y, z, speed, extrusion_mm3_per_mm);
|
||||
return;
|
||||
}
|
||||
|
||||
//Increase the extrusion amount to calculate the amount of filament used.
|
||||
Point3 diff = Point3(x,y,z) - getPosition();
|
||||
|
||||
extrusion_amount += extrusion_per_mm * diff.vSizeMM();
|
||||
}else{
|
||||
//If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
|
||||
if (!isRetracted)
|
||||
{
|
||||
*output_stream << "M103\r\n";
|
||||
isRetracted = true;
|
||||
}
|
||||
double extrusion_per_mm = extrusion_mm3_per_mm;
|
||||
if (!is_volumatric)
|
||||
{
|
||||
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
|
||||
Point gcode_pos = getGcodePos(x,y, current_extruder);
|
||||
|
||||
if (extrusion_mm3_per_mm > 0.000001)
|
||||
{
|
||||
Point3 diff = Point3(x,y,z) - getPosition();
|
||||
if (isZHopped > 0)
|
||||
{
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
|
||||
isZHopped = 0;
|
||||
}
|
||||
*output_stream << std::setprecision(3) <<
|
||||
"G1 X" << INT2MM(gcode_pos.X) <<
|
||||
" Y" << INT2MM(gcode_pos.Y) <<
|
||||
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n";
|
||||
double prime_volume = extruder_attr[current_extruder].prime_volume;
|
||||
current_e_value += (is_volumatric) ? prime_volume : prime_volume / extruder_attr[current_extruder].filament_area;
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
{
|
||||
if (firmware_retract)
|
||||
{ // note that BFB is handled differently
|
||||
*output_stream << "G11\n";
|
||||
//Assume default UM2 retraction settings.
|
||||
if (prime_volume > 0)
|
||||
{
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
}
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), 25.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
current_e_value += extruder_attr[current_extruder].retraction_e_amount_current;
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
|
||||
}
|
||||
if (getCurrentExtrudedVolume() > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
|
||||
{
|
||||
resetExtrusionValue();
|
||||
}
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = 0.0;
|
||||
}
|
||||
else if (prime_volume > 0.0)
|
||||
{
|
||||
current_e_value += prime_volume;
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
|
||||
}
|
||||
extruder_attr[current_extruder].prime_volume = 0.0;
|
||||
current_e_value += extrusion_per_mm * diff.vSizeMM();
|
||||
*output_stream << "G1";
|
||||
}
|
||||
else
|
||||
{
|
||||
//Normal E handling.
|
||||
|
||||
if (extrusion_mm3_per_mm > 0.000001)
|
||||
*output_stream << "G0";
|
||||
|
||||
if (commandSocket)
|
||||
{
|
||||
Point3 diff = Point3(x,y,z) - getPosition();
|
||||
if (isZHopped > 0)
|
||||
{
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
|
||||
isZHopped = false;
|
||||
}
|
||||
extrusion_amount += (is_volumatric) ? last_coasted_amount_mm3 : last_coasted_amount_mm3 / getFilamentArea(current_extruder);
|
||||
if (isRetracted)
|
||||
{
|
||||
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
|
||||
{
|
||||
*output_stream << "G11\n";
|
||||
//Assume default UM2 retraction settings.
|
||||
if (last_coasted_amount_mm3 > 0)
|
||||
{
|
||||
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
|
||||
}
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0);
|
||||
}else{
|
||||
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
|
||||
currentSpeed = retractionPrimeSpeed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed);
|
||||
}
|
||||
if (getExtrusionAmountMM3(current_extruder) > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
|
||||
resetExtrusionValue();
|
||||
isRetracted = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (last_coasted_amount_mm3 > 0)
|
||||
{
|
||||
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed);
|
||||
}
|
||||
}
|
||||
last_coasted_amount_mm3 = 0;
|
||||
extrusion_amount += extrusion_per_mm * diff.vSizeMM();
|
||||
*output_stream << "G1";
|
||||
}else{
|
||||
*output_stream << "G0";
|
||||
}
|
||||
|
||||
if (currentSpeed != speed)
|
||||
{
|
||||
*output_stream << " F" << (speed * 60);
|
||||
currentSpeed = speed;
|
||||
}
|
||||
|
||||
*output_stream << std::setprecision(3) <<
|
||||
" X" << INT2MM(gcode_pos.X) <<
|
||||
" Y" << INT2MM(gcode_pos.Y);
|
||||
if (z != currentPosition.z)
|
||||
*output_stream << " Z" << INT2MM(z);
|
||||
if (extrusion_mm3_per_mm > 0.000001)
|
||||
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount;
|
||||
*output_stream << "\n";
|
||||
// we should send this travel as a non-retraction move
|
||||
cura::Polygons travelPoly;
|
||||
PolygonRef travel = travelPoly.newPoly();
|
||||
travel.add(Point(currentPosition.x, currentPosition.y));
|
||||
travel.add(Point(x, y));
|
||||
commandSocket->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
|
||||
}
|
||||
}
|
||||
|
||||
if (currentSpeed != speed)
|
||||
{
|
||||
*output_stream << " F" << (speed * 60);
|
||||
currentSpeed = speed;
|
||||
}
|
||||
|
||||
*output_stream << std::setprecision(3) <<
|
||||
" X" << INT2MM(gcode_pos.X) <<
|
||||
" Y" << INT2MM(gcode_pos.Y);
|
||||
if (z != currentPosition.z + isZHopped)
|
||||
*output_stream << " Z" << INT2MM(z + isZHopped);
|
||||
if (extrusion_mm3_per_mm > 0.000001)
|
||||
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value;
|
||||
*output_stream << "\n";
|
||||
|
||||
currentPosition = Point3(x, y, z);
|
||||
startPosition = currentPosition;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), speed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed);
|
||||
}
|
||||
|
||||
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
{
|
||||
if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction.
|
||||
return;
|
||||
if (isRetracted)
|
||||
return;
|
||||
if (config->amount <= 0)
|
||||
return;
|
||||
|
||||
if (!force && config->retraction_count_max > 0 && int(extrusion_amount_at_previous_n_retractions.size()) == config->retraction_count_max - 1
|
||||
&& extrusion_amount < extrusion_amount_at_previous_n_retractions.back() + config->retraction_extrusion_window)
|
||||
return;
|
||||
|
||||
if (config->primeAmount > 0)
|
||||
{
|
||||
extrusion_amount += config->primeAmount;
|
||||
return;
|
||||
}
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current == config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (config->distance <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
{ // handle retraction limitation
|
||||
double current_extruded_volume = getCurrentExtrudedVolume();
|
||||
std::deque<double>& extruded_volume_at_previous_n_retractions = extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions;
|
||||
while (int(extruded_volume_at_previous_n_retractions.size()) >= config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
|
||||
{
|
||||
// extruder switch could have introduced data which falls outside the retraction window
|
||||
// also the retraction_count_max could have changed between the last retraction and this
|
||||
extruded_volume_at_previous_n_retractions.pop_back();
|
||||
}
|
||||
if (!force && config->retraction_count_max <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max - 1
|
||||
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extruder_attr[current_extruder].filament_area)
|
||||
{
|
||||
return;
|
||||
}
|
||||
extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
|
||||
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max)
|
||||
{
|
||||
extruded_volume_at_previous_n_retractions.pop_back();
|
||||
}
|
||||
}
|
||||
retractionPrimeSpeed = config->primeSpeed;
|
||||
|
||||
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
|
||||
extruder_attr[current_extruder].last_retraction_prime_speed = config->primeSpeed;
|
||||
|
||||
double retraction_e_amount = config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
|
||||
if (firmware_retract)
|
||||
{
|
||||
*output_stream << "G10\n";
|
||||
//Assume default UM2 retraction settings.
|
||||
double retraction_distance = 4.5;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount - retraction_distance), 25); // TODO: hardcoded values!
|
||||
}else{
|
||||
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount - config->amount << "\n";
|
||||
currentSpeed = config->speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount - config->amount), currentSpeed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value - retraction_e_amount), 25); // TODO: hardcoded values!
|
||||
}
|
||||
else
|
||||
{
|
||||
current_e_value -= retraction_e_amount;
|
||||
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
currentSpeed = config->speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
|
||||
}
|
||||
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount ;
|
||||
extruder_attr[current_extruder].prime_volume += config->prime_volume;
|
||||
|
||||
if (config->zHop > 0)
|
||||
{
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + config->zHop) << "\n";
|
||||
isZHopped = true;
|
||||
isZHopped = config->zHop;
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << "\n";
|
||||
}
|
||||
extrusion_amount_at_previous_n_retractions.push_front(extrusion_amount);
|
||||
if (int(extrusion_amount_at_previous_n_retractions.size()) == config->retraction_count_max)
|
||||
{
|
||||
extrusion_amount_at_previous_n_retractions.pop_back();
|
||||
}
|
||||
isRetracted = true;
|
||||
}
|
||||
|
||||
void GCodeExport::writeRetraction_extruderSwitch()
|
||||
{
|
||||
if (isRetracted) { return; }
|
||||
|
||||
if (flavor == EGCodeFlavor::BFB)
|
||||
{
|
||||
if (!isRetracted)
|
||||
if (!extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
*output_stream << "M103\r\n";
|
||||
|
||||
isRetracted = true;
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 is a stub; BFB doesn't use the actual retracted amount; retraction is performed by firmware
|
||||
return;
|
||||
}
|
||||
resetExtrusionValue();
|
||||
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
|
||||
|
||||
double retraction_e_amount = extruder_attr[current_extruder].extruder_switch_retraction_distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current == retraction_e_amount)
|
||||
{
|
||||
*output_stream << "G10 S1\n";
|
||||
}else{
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].extruderSwitchRetractionSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << (extrusion_amount - extruder_attr[current_extruder].extruderSwitchRetraction) << "\n";
|
||||
currentSpeed = extruder_attr[current_extruder].extruderSwitchRetractionSpeed;
|
||||
return;
|
||||
}
|
||||
isRetracted = true;
|
||||
|
||||
double current_extruded_volume = getCurrentExtrudedVolume();
|
||||
std::deque<double>& extruded_volume_at_previous_n_retractions = extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions;
|
||||
extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
|
||||
|
||||
if (firmware_retract)
|
||||
{
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
{
|
||||
return;
|
||||
}
|
||||
*output_stream << "G10 S1\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
current_e_value -= retraction_e_amount;
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].extruderSwitchRetractionSpeed * 60) << " "
|
||||
<< extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
// the E value of the extruder switch retraction 'overwrites' the E value of the normal retraction
|
||||
currentSpeed = extruder_attr[current_extruder].extruderSwitchRetractionSpeed;
|
||||
extruder_attr[current_extruder].last_retraction_prime_speed = extruder_attr[current_extruder].extruderSwitchPrimeSpeed;
|
||||
}
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount; // suppose that for UM2 the retraction amount in the firmware is equal to the provided amount
|
||||
}
|
||||
|
||||
void GCodeExport::switchExtruder(int new_extruder)
|
||||
{
|
||||
if (current_extruder == new_extruder)
|
||||
return;
|
||||
|
||||
if (!isRetracted) // assumes the last retraction already was an extruder switch retraction
|
||||
{
|
||||
writeRetraction_extruderSwitch();
|
||||
}
|
||||
|
||||
|
||||
writeRetraction_extruderSwitch();
|
||||
|
||||
resetExtrusionValue(); // should be called on the old extruder
|
||||
|
||||
int old_extruder = current_extruder;
|
||||
current_extruder = new_extruder;
|
||||
|
||||
if (flavor == EGCodeFlavor::MACH3)
|
||||
resetExtrusionValue();
|
||||
isRetracted = true;
|
||||
{
|
||||
resetExtrusionValue(); // also zero the E value on the new extruder
|
||||
}
|
||||
|
||||
writeCode(extruder_attr[old_extruder].end_code.c_str());
|
||||
if (flavor == EGCodeFlavor::MAKERBOT)
|
||||
{
|
||||
*output_stream << "M135 T" << current_extruder << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
*output_stream << "T" << current_extruder << "\n";
|
||||
}
|
||||
writeCode(extruder_attr[new_extruder].start_code.c_str());
|
||||
|
||||
//Change the Z position so it gets re-writting again. We do not know if the switch code modified the Z position.
|
||||
@@ -494,11 +591,9 @@ void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
|
||||
*output_stream << temperature << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::finalize(int maxObjectHeight, double moveSpeed, const char* endCode)
|
||||
void GCodeExport::finalize(double moveSpeed, const char* endCode)
|
||||
{
|
||||
writeFanCommand(0);
|
||||
setZ(maxObjectHeight + 5000);
|
||||
writeMove(Point3(0,0,maxObjectHeight + 5000) + getPositionXY(), moveSpeed, 0);
|
||||
writeCode(endCode);
|
||||
log("Print time: %d\n", int(getTotalPrintTime()));
|
||||
log("Filament: %d\n", int(getTotalFilamentUsed(0)));
|
||||
@@ -509,3 +604,4 @@ void GCodeExport::finalize(int maxObjectHeight, double moveSpeed, const char* en
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
+96
-55
@@ -8,9 +8,10 @@
|
||||
|
||||
#include "settings.h"
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/NoCopy.h"
|
||||
#include "timeEstimate.h"
|
||||
#include "MeshGroup.h"
|
||||
#include "PrintFeature.h"
|
||||
#include "commandSocket.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
@@ -29,13 +30,13 @@ struct CoastingConfig
|
||||
class RetractionConfig
|
||||
{
|
||||
public:
|
||||
double amount; //!< The amount retracted
|
||||
double speed; //!< The speed with which to retract
|
||||
double primeSpeed; //!< the speed with which to unretract
|
||||
double primeAmount; //!< the amount of material primed after unretracting
|
||||
double distance; //!< The distance retracted (in mm)
|
||||
double speed; //!< The speed with which to retract (in mm/s)
|
||||
double primeSpeed; //!< the speed with which to unretract (in mm/s)
|
||||
double prime_volume; //!< the amount of material primed after unretracting (in mm^3)
|
||||
int zHop; //!< the amount with which to lift the head during a retraction-travel
|
||||
int retraction_min_travel_distance; //!<
|
||||
double retraction_extrusion_window;
|
||||
double retraction_extrusion_window; //!< in mm
|
||||
int retraction_count_max;
|
||||
};
|
||||
|
||||
@@ -43,62 +44,82 @@ public:
|
||||
class GCodePathConfig
|
||||
{
|
||||
private:
|
||||
double speed; //!< movement speed
|
||||
double speed_base; //!< movement speed (mm/s) specific to this print feature
|
||||
double speed_current; //!< current movement speed (mm/s) (modified by layer_nr etc.)
|
||||
int line_width; //!< width of the line extruded
|
||||
double flow; //!< extrusion flow in %
|
||||
int layer_thickness; //!< layer height
|
||||
double extrusion_mm3_per_mm;//!< mm^3 filament moved per mm line extruded
|
||||
public:
|
||||
const char* name;
|
||||
const char* name; //!< name of the feature type
|
||||
bool spiralize;
|
||||
RetractionConfig *const retraction_config;
|
||||
|
||||
// GCodePathConfig() : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(nullptr), spiralize(false), retraction_config(nullptr) {}
|
||||
GCodePathConfig(RetractionConfig* retraction_config, const char* name) : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(name), spiralize(false), retraction_config(retraction_config) {}
|
||||
GCodePathConfig(RetractionConfig* retraction_config, const char* name) : speed_base(0), speed_current(0), line_width(0), extrusion_mm3_per_mm(0.0), name(name), spiralize(false), retraction_config(retraction_config) {}
|
||||
|
||||
void setSpeed(double speed)
|
||||
{
|
||||
this->speed = speed;
|
||||
}
|
||||
|
||||
void setLineWidth(int line_width)
|
||||
/*!
|
||||
* Initialize some of the member variables.
|
||||
*
|
||||
* Warning! setLayerHeight still has to be called before this object can be used.
|
||||
*/
|
||||
void init(double speed, int line_width, double flow)
|
||||
{
|
||||
speed_base = speed;
|
||||
this->speed_current = speed;
|
||||
this->line_width = line_width;
|
||||
calculateExtrusion();
|
||||
this->flow = flow;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Set the layer height and (re)compute the extrusion_per_mm
|
||||
*/
|
||||
void setLayerHeight(int layer_height)
|
||||
{
|
||||
this->layer_thickness = layer_height;
|
||||
calculateExtrusion();
|
||||
}
|
||||
|
||||
void setFlow(double flow)
|
||||
{
|
||||
this->flow = flow;
|
||||
calculateExtrusion();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the speed to somewhere between the @p min_speed and the speed_iconic.
|
||||
*
|
||||
* This functions should not be called with @p layer_nr > @p max_speed_layer !
|
||||
*
|
||||
* \param min_speed The speed at layer zero
|
||||
* \param layer_nr The layer number
|
||||
* \param max_speed_layer The layer number for which the speed_iconic should be used.
|
||||
*/
|
||||
void smoothSpeed(double min_speed, int layer_nr, double max_speed_layer)
|
||||
{
|
||||
speed = (speed*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
|
||||
speed_current = (speed_base*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Can only be called after the layer height has been set (which is done while writing the gcode!)
|
||||
*/
|
||||
double getExtrusionMM3perMM()
|
||||
{
|
||||
return extrusion_mm3_per_mm;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the movement speed in mm/s
|
||||
*/
|
||||
double getSpeed()
|
||||
{
|
||||
return speed;
|
||||
return speed_current;
|
||||
}
|
||||
|
||||
int getLineWidth()
|
||||
{
|
||||
return line_width;
|
||||
}
|
||||
|
||||
|
||||
bool isTravelPath()
|
||||
{
|
||||
return line_width == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void calculateExtrusion()
|
||||
{
|
||||
@@ -108,7 +129,7 @@ private:
|
||||
|
||||
//The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels.
|
||||
// Any customizations on GCodes flavors are done in this class.
|
||||
class GCodeExport
|
||||
class GCodeExport : public NoCopy
|
||||
{
|
||||
private:
|
||||
struct ExtruderTrainAttributes
|
||||
@@ -119,55 +140,68 @@ private:
|
||||
std::string end_code;
|
||||
double filament_area; //!< in mm^2 for non-volumetric, cylindrical filament
|
||||
|
||||
double extruderSwitchRetraction;
|
||||
int extruderSwitchRetractionSpeed;
|
||||
int extruderSwitchPrimeSpeed;
|
||||
|
||||
double extruder_switch_retraction_distance; //<! extruder switch retraction distance in mm
|
||||
int extruderSwitchRetractionSpeed; //!< extruder switch retraction speed in mm/s
|
||||
int extruderSwitchPrimeSpeed; //!< prime speed of extruder switch in mm/s
|
||||
|
||||
double totalFilament; //!< total filament used per extruder in mm^3
|
||||
int currentTemperature;
|
||||
|
||||
|
||||
double retraction_e_amount_current; //!< The current retracted amount (in mm or mm^3), or zero(i.e. false) if it is not currently retracted (positive values mean retracted amount, so negative impact on E values)
|
||||
double retraction_e_amount_at_e_start; //!< The ExtruderTrainAttributes::retraction_amount_current value at E0, i.e. the offset (in mm or mm^3) from E0 to the situation where the filament is at the tip of the nozzle.
|
||||
|
||||
double prime_volume; //!< Amount of material (in mm^3) to be primed after an unretration (due to oozing and/or coasting)
|
||||
double last_retraction_prime_speed; //!< The last prime speed (in mm/s) of the to-be-primed amount
|
||||
|
||||
std::deque<double> extruded_volume_at_previous_n_retractions; // in mm^3
|
||||
|
||||
ExtruderTrainAttributes()
|
||||
: nozzle_offset(0,0)
|
||||
, extruderCharacter(0)
|
||||
, start_code("")
|
||||
, end_code("")
|
||||
, filament_area(0)
|
||||
, extruderSwitchRetraction(0.0)
|
||||
, extruder_switch_retraction_distance(0.0)
|
||||
, extruderSwitchRetractionSpeed(0)
|
||||
, extruderSwitchPrimeSpeed(0)
|
||||
, totalFilament(0)
|
||||
, currentTemperature(0)
|
||||
, retraction_e_amount_current(0.0)
|
||||
, retraction_e_amount_at_e_start(0.0)
|
||||
, prime_volume(0.0)
|
||||
, last_retraction_prime_speed(1.0)
|
||||
{ }
|
||||
};
|
||||
ExtruderTrainAttributes extruder_attr[MAX_EXTRUDERS];
|
||||
bool use_extruder_offset_to_offset_coords;
|
||||
|
||||
std::ostream* output_stream;
|
||||
double extrusion_amount; // in mm or mm^3
|
||||
std::deque<double> extrusion_amount_at_previous_n_retractions; // in mm or mm^3
|
||||
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
|
||||
Point3 currentPosition;
|
||||
Point3 startPosition;
|
||||
double currentSpeed;
|
||||
int zPos;
|
||||
bool isRetracted;
|
||||
bool isZHopped;
|
||||
double currentSpeed; //!< The current speed (F values / 60) in mm/s
|
||||
int zPos; // TODO: why is this different from currentPosition.z ? zPos is set every layer, while currentPosition.z is set every move. However, the z position is generally not changed within a layer!
|
||||
int isZHopped; //!< The amount by which the print head is currently z hopped, or zero if it is not z hopped. (A z hop is used during travel moves to avoid collision with other layer parts)
|
||||
|
||||
double last_coasted_amount_mm3; //!< The coasted amount of filament to be primed on the first next extrusion. (same type as GCodeExport::extrusion_amount)
|
||||
double retractionPrimeSpeed;
|
||||
int current_extruder;
|
||||
int currentFanSpeed;
|
||||
EGCodeFlavor flavor;
|
||||
|
||||
double totalPrintTime;
|
||||
double total_print_time_per_feature[(unsigned int)EPrintFeature::ENUM_COUNT];
|
||||
TimeEstimateCalculator estimateCalculator;
|
||||
|
||||
bool is_volumatric;
|
||||
bool firmware_retract; //!< whether retractions are done in the firmware, or hardcoded in E values.
|
||||
|
||||
CommandSocket* commandSocket; //!< for sending travel data
|
||||
unsigned int layer_nr; //!< for sending travel data
|
||||
|
||||
public:
|
||||
|
||||
GCodeExport();
|
||||
~GCodeExport();
|
||||
|
||||
void setCommandSocketAndLayerNr(CommandSocket* commandSocket, unsigned int layer_nr);
|
||||
|
||||
void setOutputStream(std::ostream* stream);
|
||||
|
||||
Point getExtruderOffset(int id);
|
||||
@@ -179,38 +213,41 @@ public:
|
||||
|
||||
void setZ(int z);
|
||||
|
||||
void setLastCoastedAmountMM3(double last_coasted_amount) { this->last_coasted_amount_mm3 = last_coasted_amount; }
|
||||
void addLastCoastedVolume(double last_coasted_volume)
|
||||
{
|
||||
extruder_attr[current_extruder].prime_volume += last_coasted_volume;
|
||||
}
|
||||
|
||||
Point3 getPosition();
|
||||
|
||||
Point getPositionXY();
|
||||
|
||||
void resetStartPosition();
|
||||
|
||||
Point getStartPositionXY();
|
||||
|
||||
int getPositionZ();
|
||||
|
||||
int getExtruderNr();
|
||||
|
||||
void setFilamentDiameter(unsigned int n, int diameter);
|
||||
double getFilamentArea(unsigned int extruder);
|
||||
|
||||
double getExtrusionAmountMM3(unsigned int extruder);
|
||||
double getCurrentExtrudedVolume();
|
||||
|
||||
double getTotalFilamentUsed(int e);
|
||||
|
||||
double getTotalPrintTime();
|
||||
double getTotalPrintTime(EPrintFeature print_feature);
|
||||
void updateTotalPrintTime(EPrintFeature print_feature = EPrintFeature::UNCLASSIFIED);
|
||||
void updateTotalPrintTime();
|
||||
void resetTotalPrintTimeAndFilament();
|
||||
|
||||
void writeComment(std::string comment);
|
||||
void writeTypeComment(const char* type);
|
||||
void writeLayerComment(int layer_nr);
|
||||
void writeLayerCountComment(int layer_count);
|
||||
|
||||
void writeLine(const char* line);
|
||||
|
||||
/*!
|
||||
* Reset the current_e_value to prevent too high E values.
|
||||
*
|
||||
* The current extruded volume is added to the current extruder_attr.
|
||||
*/
|
||||
void resetExtrusionValue();
|
||||
|
||||
void writeDelay(double timeAmount);
|
||||
@@ -220,6 +257,10 @@ public:
|
||||
void writeMove(Point3 p, double speed, double extrusion_per_mm);
|
||||
private:
|
||||
void writeMove(int x, int y, int z, double speed, double extrusion_per_mm);
|
||||
/*!
|
||||
* The writeMove when flavor == BFB
|
||||
*/
|
||||
void writeMoveBFB(int x, int y, int z, double speed, double extrusion_per_mm);
|
||||
public:
|
||||
void writeRetraction(RetractionConfig* config, bool force=false);
|
||||
|
||||
@@ -246,7 +287,7 @@ public:
|
||||
extruder_attr[n].start_code = train->getSettingString("machine_extruder_start_code");
|
||||
extruder_attr[n].end_code = train->getSettingString("machine_extruder_end_code");
|
||||
|
||||
extruder_attr[n].extruderSwitchRetraction = INT2MM(train->getSettingInMicrons("switch_extruder_retraction_amount"));
|
||||
extruder_attr[n].extruder_switch_retraction_distance = INT2MM(train->getSettingInMicrons("switch_extruder_retraction_amount"));
|
||||
extruder_attr[n].extruderSwitchRetractionSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_retraction_speed");
|
||||
extruder_attr[n].extruderSwitchPrimeSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_prime_speed");
|
||||
}
|
||||
@@ -254,7 +295,7 @@ public:
|
||||
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
|
||||
use_extruder_offset_to_offset_coords = settings->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
|
||||
}
|
||||
void finalize(int maxObjectHeight, double moveSpeed, const char* endCode);
|
||||
void finalize(double moveSpeed, const char* endCode);
|
||||
|
||||
};
|
||||
|
||||
|
||||
+456
-206
@@ -1,24 +1,39 @@
|
||||
#include <cstring>
|
||||
#include "gcodePlanner.h"
|
||||
#include "pathOrderOptimizer.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include <cstring>
|
||||
#include "debug.h" // debugging
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "MergeInfillLines.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
TimeMaterialEstimates TimeMaterialEstimates::operator-(const TimeMaterialEstimates& other)
|
||||
{
|
||||
return TimeMaterialEstimates(extrude_time - other.extrude_time,unretracted_travel_time - other.unretracted_travel_time,retracted_travel_time - other.retracted_travel_time,material - other.material);
|
||||
}
|
||||
|
||||
TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstimates& other)
|
||||
{
|
||||
extrude_time -= other.extrude_time;
|
||||
unretracted_travel_time -= other.unretracted_travel_time;
|
||||
retracted_travel_time -= other.retracted_travel_time;
|
||||
material -= other.material;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, float flow)
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans.back().paths;
|
||||
if (paths.size() > 0 && paths[paths.size()-1].config == config && !paths[paths.size()-1].done && paths[paths.size()-1].flow == flow)
|
||||
return &paths[paths.size()-1];
|
||||
paths.push_back(GCodePath());
|
||||
GCodePath* ret = &paths[paths.size()-1];
|
||||
ret->retract = false;
|
||||
ret->config = config;
|
||||
ret->extruder = currentExtruder;
|
||||
ret->done = false;
|
||||
ret->flow = flow;
|
||||
if (config != &travelConfig)
|
||||
if (config != &storage.travel_config)
|
||||
{
|
||||
last_retraction_config = config->retraction_config;
|
||||
}
|
||||
@@ -27,28 +42,35 @@ GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, float
|
||||
|
||||
void GCodePlanner::forceNewPathStart()
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans.back().paths;
|
||||
if (paths.size() > 0)
|
||||
paths[paths.size()-1].done = true;
|
||||
}
|
||||
|
||||
GCodePlanner::GCodePlanner(GCodeExport& gcode, SliceDataStorage& storage, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
: gcode(gcode), storage(storage)
|
||||
, travelConfig(retraction_config_travel, "MOVE")
|
||||
GCodePlanner::GCodePlanner(CommandSocket* commandSocket, SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
: storage(storage)
|
||||
, commandSocket(commandSocket)
|
||||
, layer_nr(layer_nr)
|
||||
, z(z)
|
||||
, layer_thickness(layer_thickness)
|
||||
, start_position(last_position)
|
||||
, lastPosition(last_position)
|
||||
, comb_boundary_inside(computeCombBoundaryInside())
|
||||
, fan_speed_layer_time_settings(fan_speed_layer_time_settings)
|
||||
{
|
||||
lastPosition = gcode.getPositionXY();
|
||||
travelConfig.setSpeed(travelSpeed);
|
||||
extruder_plans.reserve(storage.meshgroup->getExtruderCount());
|
||||
extruder_plans.emplace_back(current_extruder);
|
||||
comb = nullptr;
|
||||
was_inside = true; // means it will try to get inside the comb boundary first
|
||||
is_inside = true; // means it will try to get inside the comb boundary
|
||||
last_retraction_config = &storage.retraction_config; // start with general config
|
||||
setExtrudeSpeedFactor(1.0);
|
||||
setTravelSpeedFactor(1.0);
|
||||
extraTime = 0.0;
|
||||
totalPrintTime = 0.0;
|
||||
currentExtruder = gcode.getExtruderNr();
|
||||
if (retraction_combing)
|
||||
{
|
||||
was_combing = true; // means it will try to get inside the comb boundary first
|
||||
is_going_to_comb = true; // means it will try to get inside the comb boundary
|
||||
comb = new Comb(storage, layer_nr, comb_boundary_offset, travel_avoid_other_parts, travel_avoid_distance);
|
||||
comb = new Comb(storage, layer_nr, comb_boundary_inside, comb_boundary_offset, travel_avoid_other_parts, travel_avoid_distance);
|
||||
}
|
||||
else
|
||||
comb = nullptr;
|
||||
@@ -60,43 +82,90 @@ GCodePlanner::~GCodePlanner()
|
||||
delete comb;
|
||||
}
|
||||
|
||||
void GCodePlanner::setCombing(bool going_to_comb)
|
||||
Polygons GCodePlanner::computeCombBoundaryInside()
|
||||
{
|
||||
is_going_to_comb = going_to_comb;
|
||||
if (layer_nr < 0)
|
||||
{ // when a raft is present
|
||||
return storage.raftOutline.offset(MM2INT(0.1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Polygons layer_walls;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
layer.getSecondOrInnermostWalls(layer_walls);
|
||||
}
|
||||
return layer_walls;
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::setIsInside(bool _is_inside)
|
||||
{
|
||||
is_inside = _is_inside;
|
||||
}
|
||||
|
||||
|
||||
bool GCodePlanner::setExtruder(int extruder)
|
||||
{
|
||||
if (extruder == currentExtruder)
|
||||
if (extruder == extruder_plans.back().extruder)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(currentExtruder);
|
||||
bool end_pos_absolute = train->getSettingBoolean("machine_extruder_end_pos_abs");
|
||||
Point end_pos(train->getSettingInMicrons("machine_extruder_end_pos_x"), train->getSettingInMicrons("machine_extruder_end_pos_y"));
|
||||
addTravel((end_pos_absolute)? end_pos : lastPosition + end_pos);
|
||||
|
||||
currentExtruder = extruder; // the extruder switch
|
||||
|
||||
forceNewPathStart();
|
||||
|
||||
train = storage.meshgroup->getExtruderTrain(currentExtruder);
|
||||
bool start_pos_absolute = train->getSettingBoolean("machine_extruder_start_pos_abs");
|
||||
Point start_pos(train->getSettingInMicrons("machine_extruder_start_pos_x"), train->getSettingInMicrons("machine_extruder_start_pos_y"));
|
||||
lastPosition = (start_pos_absolute)? start_pos : lastPosition + start_pos;
|
||||
{ // handle end position of the prev extruder
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder_plans.back().extruder);
|
||||
bool end_pos_absolute = train->getSettingBoolean("machine_extruder_end_pos_abs");
|
||||
Point end_pos(train->getSettingInMicrons("machine_extruder_end_pos_x"), train->getSettingInMicrons("machine_extruder_end_pos_y"));
|
||||
if (!end_pos_absolute)
|
||||
{
|
||||
end_pos += lastPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
|
||||
end_pos += extruder_offset; // absolute end pos is given as a head position
|
||||
}
|
||||
addTravel(end_pos); // + extruder_offset cause it
|
||||
}
|
||||
if (extruder_plans.back().paths.empty() && extruder_plans.back().inserts.empty())
|
||||
{ // first extruder plan in a layer might be empty, cause it is made with the last extruder planned in the previous layer
|
||||
extruder_plans.back().extruder = extruder;
|
||||
}
|
||||
else
|
||||
{
|
||||
extruder_plans.emplace_back(extruder);
|
||||
}
|
||||
|
||||
// forceNewPathStart(); // automatic by the fact that we start a new ExtruderPlan
|
||||
|
||||
{ // handle starting pos of the new extruder
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
|
||||
bool start_pos_absolute = train->getSettingBoolean("machine_extruder_start_pos_abs");
|
||||
Point start_pos(train->getSettingInMicrons("machine_extruder_start_pos_x"), train->getSettingInMicrons("machine_extruder_start_pos_y"));
|
||||
if (!start_pos_absolute)
|
||||
{
|
||||
start_pos += lastPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
|
||||
start_pos += extruder_offset; // absolute start pos is given as a head position
|
||||
}
|
||||
lastPosition = start_pos;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GCodePlanner::moveInsideCombBoundary(int distance)
|
||||
{
|
||||
if (!comb) return;
|
||||
Point p = lastPosition;
|
||||
if (comb->moveInsideBoundary(&p, distance))
|
||||
int max_dist = MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were.
|
||||
// this function is to be used to move from the boudary of a part to inside the part
|
||||
Point p = lastPosition; // copy, since we are going to move p
|
||||
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist) != NO_INDEX)
|
||||
{
|
||||
//Move inside again, so we move out of tight 90deg corners
|
||||
comb->moveInsideBoundary(&p, distance);
|
||||
if (comb->inside(p))
|
||||
PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist);
|
||||
if (comb_boundary_inside.inside(p))
|
||||
{
|
||||
addTravel_simple(p);
|
||||
//Make sure the that any retraction happens after this move, not before it by starting a new move path.
|
||||
@@ -108,11 +177,14 @@ void GCodePlanner::moveInsideCombBoundary(int distance)
|
||||
void GCodePlanner::addTravel(Point p)
|
||||
{
|
||||
GCodePath* path = nullptr;
|
||||
|
||||
if (comb != nullptr && lastPosition != Point(0,0))
|
||||
|
||||
bool combed = false;
|
||||
|
||||
if (comb != nullptr && lastPosition != no_point)
|
||||
{
|
||||
CombPaths combPaths;
|
||||
if (comb->calc(lastPosition, p, combPaths, was_combing, is_going_to_comb))
|
||||
combed = comb->calc(lastPosition, p, combPaths, was_inside, is_inside, last_retraction_config->retraction_min_travel_distance);
|
||||
if (combed)
|
||||
{
|
||||
bool retract = combPaths.size() > 1;
|
||||
{ // check whether we want to retract
|
||||
@@ -129,7 +201,7 @@ void GCodePlanner::addTravel(Point p)
|
||||
|
||||
if (retract && last_retraction_config->zHop > 0)
|
||||
{ // TODO: stop comb calculation early! (as soon as we see we don't end in the same part as we began)
|
||||
path = getLatestPathWithConfig(&travelConfig);
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
|
||||
{
|
||||
path->retract = true;
|
||||
@@ -143,7 +215,7 @@ void GCodePlanner::addTravel(Point p)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
path = getLatestPathWithConfig(&travelConfig);
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
path->retract = retract;
|
||||
for (Point& combPoint : combPath)
|
||||
{
|
||||
@@ -153,25 +225,32 @@ void GCodePlanner::addTravel(Point p)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
path = getLatestPathWithConfig(&travelConfig);
|
||||
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
|
||||
{
|
||||
path->retract = true;
|
||||
}
|
||||
}
|
||||
was_combing = is_going_to_comb;
|
||||
}
|
||||
|
||||
if (!combed) {
|
||||
// no combing? always retract!
|
||||
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
|
||||
{
|
||||
if (was_inside)
|
||||
{
|
||||
ExtruderTrain* extr = storage.meshgroup->getExtruderTrain(getExtruder());
|
||||
assert (extr != nullptr);
|
||||
moveInsideCombBoundary(extr->getSettingInMicrons("machine_nozzle_size") * 1);
|
||||
}
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
path->retract = true;
|
||||
}
|
||||
}
|
||||
|
||||
addTravel_simple(p, path);
|
||||
was_inside = is_inside;
|
||||
}
|
||||
|
||||
void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
|
||||
{
|
||||
if (path == nullptr)
|
||||
{
|
||||
path = getLatestPathWithConfig(&travelConfig);
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
}
|
||||
path->points.push_back(p);
|
||||
lastPosition = p;
|
||||
@@ -250,169 +329,280 @@ void GCodePlanner::forceMinimalLayerTime(double minTime, double minimalSpeed, do
|
||||
if (minExtrudeTime < 1)
|
||||
minExtrudeTime = 1;
|
||||
double factor = extrudeTime / minExtrudeTime;
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
for(ExtruderPlan& extr_plan : extruder_plans)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
if (path->getExtrusionMM3perMM() == 0)
|
||||
continue;
|
||||
double speed = path->config->getSpeed() * factor;
|
||||
if (speed < minimalSpeed)
|
||||
factor = minimalSpeed / path->config->getSpeed();
|
||||
for (GCodePath& path : extr_plan.paths)
|
||||
{
|
||||
if (path.isTravelPath())
|
||||
continue;
|
||||
double speed = path.config->getSpeed() * factor;
|
||||
if (speed < minimalSpeed)
|
||||
factor = minimalSpeed / path.config->getSpeed();
|
||||
}
|
||||
}
|
||||
|
||||
//Only slow down with the minimal time if that will be slower then a factor already set. First layer slowdown also sets the speed factor.
|
||||
if (factor < getExtrudeSpeedFactor())
|
||||
//Only slow down for the minimal time if that will be slower.
|
||||
assert(getExtrudeSpeedFactor() == 1.0); // The extrude speed factor is assumed not to be changed yet
|
||||
if (factor < 1.0)
|
||||
{
|
||||
setExtrudeSpeedFactor(factor);
|
||||
else
|
||||
factor = getExtrudeSpeedFactor();
|
||||
|
||||
if (minTime - (extrudeTime / factor) - travelTime > 0.1)
|
||||
{
|
||||
this->extraTime = minTime - (extrudeTime / factor) - travelTime;
|
||||
}
|
||||
this->totalPrintTime = (extrudeTime / factor) + travelTime;
|
||||
else
|
||||
{
|
||||
factor = 1.0;
|
||||
}
|
||||
|
||||
double inv_factor = 1.0 / factor; // cause multiplication is faster than division
|
||||
|
||||
// Adjust stored naive time estimates
|
||||
for(ExtruderPlan& extr_plan : extruder_plans)
|
||||
{
|
||||
extr_plan.estimates.extrude_time *= inv_factor;
|
||||
for (GCodePath& path : extr_plan.paths)
|
||||
{
|
||||
path.estimates.extrude_time *= inv_factor;
|
||||
}
|
||||
}
|
||||
|
||||
if (minTime - (extrudeTime * inv_factor) - travelTime > 0.1)
|
||||
{
|
||||
this->extraTime = minTime - (extrudeTime * inv_factor) - travelTime;
|
||||
}
|
||||
this->totalPrintTime = (extrudeTime * inv_factor) + travelTime;
|
||||
}else{
|
||||
this->totalPrintTime = totalTime;
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::getNaiveTimeEstimates(double& travelTime, double& extrudeTime)
|
||||
TimeMaterialEstimates GCodePlanner::computeNaiveTimeEstimates()
|
||||
{
|
||||
travelTime = 0.0;
|
||||
extrudeTime = 0.0;
|
||||
Point p0 = gcode.getPositionXY();
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
TimeMaterialEstimates ret;
|
||||
Point p0 = start_position;
|
||||
|
||||
bool was_retracted = false; // wrong assumption; won't matter that much. (TODO)
|
||||
for(ExtruderPlan& extr_plan : extruder_plans)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
for (GCodePath& path : extr_plan.paths)
|
||||
{
|
||||
double thisTime = vSizeMM(p0 - path->points[i]) / path->config->getSpeed();
|
||||
if (path->getExtrusionMM3perMM() != 0)
|
||||
extrudeTime += thisTime;
|
||||
else
|
||||
travelTime += thisTime;
|
||||
p0 = path->points[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness)
|
||||
{
|
||||
GCodePathConfig* last_extrusion_config = nullptr;
|
||||
int extruder = gcode.getExtruderNr();
|
||||
|
||||
|
||||
for(unsigned int path_idx = 0; path_idx < paths.size(); path_idx++)
|
||||
{
|
||||
GCodePath& path = paths[path_idx];
|
||||
if (extruder != path.extruder)
|
||||
{
|
||||
extruder = path.extruder;
|
||||
gcode.switchExtruder(extruder);
|
||||
}else if (path.retract)
|
||||
{
|
||||
writeRetraction(path_idx);
|
||||
}
|
||||
if (path.config != &travelConfig && last_extrusion_config != path.config)
|
||||
{
|
||||
gcode.writeTypeComment(path.config->name);
|
||||
last_extrusion_config = path.config;
|
||||
}
|
||||
double speed = path.config->getSpeed();
|
||||
|
||||
if (path.getExtrusionMM3perMM() != 0)// Only apply the extrudeSpeed to extrusion moves
|
||||
speed *= getExtrudeSpeedFactor();
|
||||
else
|
||||
speed *= getExtrudeSpeedFactor();
|
||||
|
||||
int64_t nozzle_size = 400; // TODO allow the machine settings to be passed on everywhere :: depends on which nozzle!
|
||||
|
||||
if (MergeInfillLines(gcode, paths, travelConfig, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
|
||||
{ // !! has effect on path_idx !!
|
||||
// works when path_idx is the index of the travel move BEFORE the infill lines to be merged
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path.config == &travelConfig)
|
||||
{ // early comp for travel paths, which are handled more simply
|
||||
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
|
||||
bool is_extrusion_path = false;
|
||||
double* path_time_estimate;
|
||||
double& material_estimate = path.estimates.material;
|
||||
if (!path.isTravelPath())
|
||||
{
|
||||
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
|
||||
is_extrusion_path = true;
|
||||
path_time_estimate = &path.estimates.extrude_time;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bool spiralize = path.config->spiralize;
|
||||
if (spiralize)
|
||||
{
|
||||
//Check if we are the last spiralize path in the list, if not, do not spiralize.
|
||||
for(unsigned int m=path_idx+1; m<paths.size(); m++)
|
||||
else
|
||||
{
|
||||
if (paths[m].config->spiralize)
|
||||
spiralize = false;
|
||||
}
|
||||
}
|
||||
if (!spiralize) // normal (extrusion) move (with coasting
|
||||
{
|
||||
CoastingConfig& coasting_config = storage.coasting_config[extruder];
|
||||
bool coasting = coasting_config.coasting_enable;
|
||||
if (coasting)
|
||||
{
|
||||
coasting = writePathWithCoasting(path_idx, layerThickness
|
||||
, coasting_config.coasting_volume_move, coasting_config.coasting_speed_move, coasting_config.coasting_min_volume_move
|
||||
, coasting_config.coasting_volume_retract, coasting_config.coasting_speed_retract, coasting_config.coasting_min_volume_retract);
|
||||
}
|
||||
if (! coasting) // not same as 'else', cause we might have changed coasting in the line above...
|
||||
{ // normal path to gcode algorithm
|
||||
if ( // change |||||| to /\/\/\/\/ ...
|
||||
false &&
|
||||
path_idx + 2 < paths.size() // has a next move
|
||||
&& paths[path_idx+1].points.size() == 1 // is single extruded line
|
||||
&& paths[path_idx+1].config != &travelConfig // next move is extrusion
|
||||
&& paths[path_idx+2].config == &travelConfig // next next move is travel
|
||||
&& shorterThen(path.points.back() - gcode.getPositionXY(), 2 * nozzle_size) // preceding extrusion is close by
|
||||
&& shorterThen(paths[path_idx+1].points.back() - path.points.back(), 2 * nozzle_size) // extrusion move is small
|
||||
&& shorterThen(paths[path_idx+2].points.back() - paths[path_idx+1].points.back(), 2 * nozzle_size) // consecutive extrusion is close by
|
||||
)
|
||||
if (path.retract)
|
||||
{
|
||||
gcode.writeMove(paths[path_idx+2].points.back(), speed, paths[path_idx+1].getExtrusionMM3perMM());
|
||||
path_idx += 2;
|
||||
path_time_estimate = &path.estimates.retracted_travel_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
|
||||
path_time_estimate = &path.estimates.unretracted_travel_time;
|
||||
}
|
||||
if (path.retract != was_retracted)
|
||||
{ // handle retraction times
|
||||
double retract_unretract_time;
|
||||
RetractionConfig& retraction_config = *path.config->retraction_config;
|
||||
if (path.retract)
|
||||
{
|
||||
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
|
||||
retract_unretract_time = retraction_config.distance / retraction_config.speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
retract_unretract_time = retraction_config.distance / retraction_config.primeSpeed;
|
||||
}
|
||||
path.estimates.retracted_travel_time += 0.5 * retract_unretract_time;
|
||||
path.estimates.unretracted_travel_time += 0.5 * retract_unretract_time;
|
||||
}
|
||||
}
|
||||
for(Point& p1 : path.points)
|
||||
{
|
||||
double length = vSizeMM(p0 - p1);
|
||||
if (is_extrusion_path)
|
||||
{
|
||||
material_estimate += length * INT2MM(layer_thickness) * INT2MM(path.config->getLineWidth());
|
||||
}
|
||||
double thisTime = length / path.config->getSpeed();
|
||||
*path_time_estimate += thisTime;
|
||||
p0 = p1;
|
||||
}
|
||||
extr_plan.estimates += path.estimates;
|
||||
}
|
||||
ret += extr_plan.estimates;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GCodePlanner::processFanSpeedAndMinimalLayerTime()
|
||||
{
|
||||
FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
|
||||
TimeMaterialEstimates estimates = computeNaiveTimeEstimates();
|
||||
forceMinimalLayerTime(fsml.cool_min_layer_time, fsml.cool_min_speed, estimates.getTravelTime(), estimates.getExtrudeTime());
|
||||
|
||||
// interpolate fan speed (for cool_fan_full_layer and for cool_min_layer_time_fan_speed_max)
|
||||
fan_speed = fsml.cool_fan_speed_min;
|
||||
double totalLayerTime = estimates.unretracted_travel_time + estimates.extrude_time;
|
||||
if (totalLayerTime < fsml.cool_min_layer_time)
|
||||
{
|
||||
fan_speed = fsml.cool_fan_speed_max;
|
||||
}
|
||||
else if (totalLayerTime < fsml.cool_min_layer_time_fan_speed_max)
|
||||
{
|
||||
// when forceMinimalLayerTime didn't change the extrusionSpeedFactor, we adjust the fan speed
|
||||
fan_speed = fsml.cool_fan_speed_max - (fsml.cool_fan_speed_max-fsml.cool_fan_speed_min) * (totalLayerTime - fsml.cool_min_layer_time) / (fsml.cool_min_layer_time_fan_speed_max - fsml.cool_min_layer_time);
|
||||
}
|
||||
if (layer_nr < fsml.cool_fan_full_layer)
|
||||
{
|
||||
//Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0.
|
||||
fan_speed = fan_speed * layer_nr / fsml.cool_fan_full_layer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int layerThickness)
|
||||
{
|
||||
completeConfigs();
|
||||
|
||||
gcode.setCommandSocketAndLayerNr(commandSocket, layer_nr);
|
||||
|
||||
gcode.writeLayerComment(layer_nr);
|
||||
|
||||
gcode.setZ(z);
|
||||
|
||||
gcode.writeFanCommand(fan_speed);
|
||||
|
||||
GCodePathConfig* last_extrusion_config = nullptr;
|
||||
int extruder = gcode.getExtruderNr();
|
||||
|
||||
for(unsigned int extruder_plan_idx = 0; extruder_plan_idx < extruder_plans.size(); extruder_plan_idx++)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx];
|
||||
if (extruder != extruder_plan.extruder)
|
||||
{
|
||||
extruder = extruder_plan.extruder;
|
||||
gcode.switchExtruder(extruder);
|
||||
}
|
||||
std::vector<GCodePath>& paths = extruder_plan.paths;
|
||||
|
||||
extruder_plan.inserts.sort([](const NozzleTempInsert& a, const NozzleTempInsert& b) -> bool {
|
||||
return a.path_idx < b.path_idx;
|
||||
} );
|
||||
|
||||
for(unsigned int path_idx = 0; path_idx < paths.size(); path_idx++)
|
||||
{
|
||||
extruder_plan.handleInserts(path_idx, gcode);
|
||||
|
||||
GCodePath& path = paths[path_idx];
|
||||
if (path.retract)
|
||||
{
|
||||
writeRetraction(gcode, extruder_plan_idx, path_idx);
|
||||
}
|
||||
if (path.config != &storage.travel_config && last_extrusion_config != path.config)
|
||||
{
|
||||
gcode.writeTypeComment(path.config->name);
|
||||
last_extrusion_config = path.config;
|
||||
}
|
||||
double speed = path.config->getSpeed();
|
||||
|
||||
if (path.isTravelPath())// Only apply the extrudeSpeed to extrusion moves
|
||||
speed *= getTravelSpeedFactor();
|
||||
else
|
||||
speed *= getExtrudeSpeedFactor();
|
||||
|
||||
int64_t nozzle_size = 400; // TODO allow the machine settings to be passed on everywhere :: depends on which nozzle!
|
||||
|
||||
if (MergeInfillLines(gcode, paths, extruder_plan, storage.travel_config, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
|
||||
{ // !! has effect on path_idx !!
|
||||
// works when path_idx is the index of the travel move BEFORE the infill lines to be merged
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path.config == &storage.travel_config)
|
||||
{ // early comp for travel paths, which are handled more simply
|
||||
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
|
||||
{
|
||||
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bool spiralize = path.config->spiralize;
|
||||
if (spiralize)
|
||||
{
|
||||
//Check if we are the last spiralize path in the list, if not, do not spiralize.
|
||||
for(unsigned int m=path_idx+1; m<paths.size(); m++)
|
||||
{
|
||||
if (paths[m].config->spiralize)
|
||||
spiralize = false;
|
||||
}
|
||||
}
|
||||
if (!spiralize) // normal (extrusion) move (with coasting
|
||||
{
|
||||
CoastingConfig& coasting_config = storage.coasting_config[extruder];
|
||||
bool coasting = coasting_config.coasting_enable;
|
||||
if (coasting)
|
||||
{
|
||||
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layerThickness
|
||||
, coasting_config.coasting_volume_move, coasting_config.coasting_speed_move, coasting_config.coasting_min_volume_move
|
||||
, coasting_config.coasting_volume_retract, coasting_config.coasting_speed_retract, coasting_config.coasting_min_volume_retract);
|
||||
}
|
||||
if (! coasting) // not same as 'else', cause we might have changed [coasting] in the line above...
|
||||
{ // normal path to gcode algorithm
|
||||
if ( // change |||||| to /\/\/\/\/ ...
|
||||
false &&
|
||||
path_idx + 2 < paths.size() // has a next move
|
||||
&& paths[path_idx+1].points.size() == 1 // is single extruded line
|
||||
&& paths[path_idx+1].config != &storage.travel_config // next move is extrusion
|
||||
&& paths[path_idx+2].config == &storage.travel_config // next next move is travel
|
||||
&& shorterThen(path.points.back() - gcode.getPositionXY(), 2 * nozzle_size) // preceding extrusion is close by
|
||||
&& shorterThen(paths[path_idx+1].points.back() - path.points.back(), 2 * nozzle_size) // extrusion move is small
|
||||
&& shorterThen(paths[path_idx+2].points.back() - paths[path_idx+1].points.back(), 2 * nozzle_size) // consecutive extrusion is close by
|
||||
)
|
||||
{
|
||||
gcode.writeMove(paths[path_idx+2].points.back(), speed, paths[path_idx+1].getExtrusionMM3perMM());
|
||||
path_idx += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
|
||||
{
|
||||
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // SPIRALIZE
|
||||
//If we need to spiralize then raise the head slowly by 1 layer as this path progresses.
|
||||
float totalLength = 0.0;
|
||||
int z = gcode.getPositionZ();
|
||||
Point p0 = gcode.getPositionXY();
|
||||
for(unsigned int i=0; i<path.points.size(); i++)
|
||||
{
|
||||
Point p1 = path.points[i];
|
||||
totalLength += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
else
|
||||
{ // SPIRALIZE
|
||||
//If we need to spiralize then raise the head slowly by 1 layer as this path progresses.
|
||||
float totalLength = 0.0;
|
||||
int z = gcode.getPositionZ();
|
||||
Point p0 = gcode.getPositionXY();
|
||||
for(unsigned int i=0; i<path.points.size(); i++)
|
||||
{
|
||||
Point p1 = path.points[i];
|
||||
totalLength += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
float length = 0.0;
|
||||
p0 = gcode.getPositionXY();
|
||||
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
|
||||
{
|
||||
Point p1 = path.points[point_idx];
|
||||
length += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
gcode.setZ(z + layerThickness * length / totalLength);
|
||||
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
|
||||
float length = 0.0;
|
||||
p0 = gcode.getPositionXY();
|
||||
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
|
||||
{
|
||||
Point p1 = path.points[point_idx];
|
||||
length += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
gcode.setZ(z + layerThickness * length / totalLength);
|
||||
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extruder_plan.handleAllRemainingInserts(gcode);
|
||||
}
|
||||
|
||||
|
||||
gcode.updateTotalPrintTime();
|
||||
if (liftHeadIfNeeded && extraTime > 0.0)
|
||||
{
|
||||
@@ -420,36 +610,85 @@ void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness)
|
||||
if (last_extrusion_config)
|
||||
{
|
||||
bool extruder_switch_retract = false;// TODO: check whether we should do a retractoin_extruderSwitch; is the next path with a different extruder?
|
||||
writeRetraction(extruder_switch_retract, last_extrusion_config->retraction_config);
|
||||
writeRetraction(gcode, extruder_switch_retract, last_extrusion_config->retraction_config);
|
||||
}
|
||||
gcode.setZ(gcode.getPositionZ() + MM2INT(3.0));
|
||||
gcode.writeMove(gcode.getPositionXY(), travelConfig.getSpeed(), 0);
|
||||
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), travelConfig.getSpeed(), 0);
|
||||
gcode.writeMove(gcode.getPositionXY(), storage.travel_config.getSpeed(), 0);
|
||||
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), storage.travel_config.getSpeed(), 0); // TODO: is this safe?! wouldn't the head move into the sides then?!
|
||||
gcode.writeDelay(extraTime);
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::writeRetraction(unsigned int path_idx_travel_after)
|
||||
void GCodePlanner::completeConfigs()
|
||||
{
|
||||
if (makeRetractSwitchRetract(path_idx_travel_after))
|
||||
storage.support_config.setLayerHeight(layer_thickness);
|
||||
storage.support_roof_config.setLayerHeight(layer_thickness);
|
||||
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh.inset0_config.setLayerHeight(layer_thickness);
|
||||
mesh.insetX_config.setLayerHeight(layer_thickness);
|
||||
mesh.wall_reinforcement_config.setLayerHeight(layer_thickness);
|
||||
mesh.skin_config.setLayerHeight(layer_thickness);
|
||||
mesh.wall_reinforcement_config.setLayerHeight(layer_thickness);
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].setLayerHeight(layer_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
storage.primeTower.setConfigs(storage.meshgroup, layer_thickness);
|
||||
|
||||
processInitialLayersSpeedup();
|
||||
}
|
||||
|
||||
|
||||
void GCodePlanner::processInitialLayersSpeedup()
|
||||
{
|
||||
double initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
|
||||
if (static_cast<int>(layer_nr) < initial_speedup_layers)
|
||||
{
|
||||
double initial_layer_speed = storage.getSettingInMillimetersPerSecond("speed_layer_0");
|
||||
storage.support_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
storage.support_roof_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
initial_layer_speed = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
|
||||
mesh.inset0_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.insetX_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.skin_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::writeRetraction(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx_travel_after)
|
||||
{
|
||||
if (makeRetractSwitchRetract(gcode, extruder_plan_idx, path_idx_travel_after))
|
||||
{
|
||||
gcode.writeRetraction_extruderSwitch();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
|
||||
RetractionConfig* extrusion_retraction_config = nullptr;
|
||||
for(int extrusion_path_idx = int(path_idx_travel_after) - 1; extrusion_path_idx >= 0; extrusion_path_idx--)
|
||||
{ // backtrack to find the last extrusion path
|
||||
if (paths[extrusion_path_idx].config != &travelConfig)
|
||||
if (paths[extrusion_path_idx].config != &storage.travel_config)
|
||||
{
|
||||
extrusion_retraction_config = paths[extrusion_path_idx].config->retraction_config;
|
||||
break;
|
||||
}
|
||||
}
|
||||
writeRetraction(false, extrusion_retraction_config);
|
||||
writeRetraction(gcode, false, extrusion_retraction_config);
|
||||
}
|
||||
}
|
||||
void GCodePlanner::writeRetraction(bool extruder_switch_retract, RetractionConfig* retraction_config)
|
||||
void GCodePlanner::writeRetraction(GCodeExport& gcode, bool extruder_switch_retract, RetractionConfig* retraction_config)
|
||||
{
|
||||
if (extruder_switch_retract)
|
||||
{
|
||||
@@ -463,35 +702,46 @@ void GCodePlanner::writeRetraction(bool extruder_switch_retract, RetractionConfi
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode.writeRetraction(travelConfig.retraction_config);
|
||||
gcode.writeRetraction(storage.travel_config.retraction_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GCodePlanner::makeRetractSwitchRetract(unsigned int path_idx)
|
||||
bool GCodePlanner::makeRetractSwitchRetract(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx)
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
|
||||
for (unsigned int path_idx2 = path_idx + 1; path_idx2 < paths.size(); path_idx2++)
|
||||
{
|
||||
if (paths[path_idx2].getExtrusionMM3perMM() > 0)
|
||||
{
|
||||
if (paths[path_idx2].extruder != gcode.getExtruderNr())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
if (extruder_plans.size() <= extruder_plan_idx+1)
|
||||
{
|
||||
return false; // TODO: check first extruder of the next layer! (generally only on the last layer of the second extruder)
|
||||
}
|
||||
|
||||
if (extruder_plans[extruder_plan_idx + 1].extruder != extruder_plans[extruder_plan_idx].extruder)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GCodePlanner::writePathWithCoasting(unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract)
|
||||
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract)
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
|
||||
GCodePath& path = paths[path_idx];
|
||||
if (path_idx + 1 >= paths.size()
|
||||
||
|
||||
! (path.getExtrusionMM3perMM() > 0.0 && paths[path_idx + 1].config->getExtrusionMM3perMM() == 0.0)
|
||||
! (!path.isTravelPath() && paths[path_idx + 1].config->isTravelPath())
|
||||
||
|
||||
path.points.size() < 2
|
||||
)
|
||||
@@ -503,15 +753,15 @@ bool GCodePlanner::writePathWithCoasting(unsigned int path_idx, int64_t layerThi
|
||||
if (path_next.retract)
|
||||
{
|
||||
if (coasting_volume_retract <= 0) { return false; }
|
||||
return writePathWithCoasting(path, path_next, layerThickness, coasting_volume_retract, coasting_speed_retract, coasting_min_volume_retract, makeRetractSwitchRetract(path_idx));
|
||||
return writePathWithCoasting(gcode, path, path_next, layerThickness, coasting_volume_retract, coasting_speed_retract, coasting_min_volume_retract, makeRetractSwitchRetract(gcode, extruder_plan_idx, path_idx));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (coasting_volume_move <= 0) { return false; }
|
||||
return writePathWithCoasting(path, path_next, layerThickness, coasting_volume_move, coasting_speed_move, coasting_min_volume_move);
|
||||
return writePathWithCoasting(gcode, path, path_next, layerThickness, coasting_volume_move, coasting_speed_move, coasting_min_volume_move);
|
||||
}
|
||||
}
|
||||
bool GCodePlanner::writePathWithCoasting(GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract)
|
||||
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract)
|
||||
{
|
||||
|
||||
int64_t coasting_min_dist_considered = 100; // hardcoded setting for when to not perform coasting
|
||||
@@ -599,7 +849,7 @@ bool GCodePlanner::writePathWithCoasting(GCodePath& path, GCodePath& path_next,
|
||||
|
||||
if (path_next.retract)
|
||||
{
|
||||
writeRetraction(extruder_switch_retract, path.config->retraction_config);
|
||||
writeRetraction(gcode, extruder_switch_retract, path.config->retraction_config);
|
||||
}
|
||||
|
||||
for (unsigned int point_idx = point_idx_before_start + 1; point_idx < path.points.size(); point_idx++)
|
||||
@@ -607,7 +857,7 @@ bool GCodePlanner::writePathWithCoasting(GCodePath& path, GCodePath& path_next,
|
||||
gcode.writeMove(path.points[point_idx], coasting_speed * path.config->getSpeed(), 0);
|
||||
}
|
||||
|
||||
gcode.setLastCoastedAmountMM3(path.getExtrusionMM3perMM() * INT2MM(actual_coasting_dist));
|
||||
gcode.addLastCoastedVolume(path.getExtrusionMM3perMM() * INT2MM(actual_coasting_dist));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+315
-33
@@ -8,6 +8,8 @@
|
||||
#include "utils/polygon.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "wallOverlap.h"
|
||||
#include "commandSocket.h"
|
||||
#include "FanSpeedLayerTime.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
@@ -15,46 +17,251 @@ namespace cura
|
||||
|
||||
class SliceDataStorage;
|
||||
|
||||
/*!
|
||||
* A gcode command to insert before a specific path.
|
||||
*
|
||||
* Currently only used for preheat commands
|
||||
*/
|
||||
struct NozzleTempInsert
|
||||
{
|
||||
const unsigned int path_idx; //!< The path before which to insert this command
|
||||
double time_after_path_start; //!< The time after the start of the path, before which to insert the command // TODO: use this to insert command in between moves in a path!
|
||||
int extruder; //!< The extruder for which to set the temp
|
||||
double temperature; //!< The temperature of the temperature command to insert
|
||||
bool wait; //!< Whether to wait for the temperature to be reached
|
||||
NozzleTempInsert(unsigned int path_idx, int extruder, double temperature, bool wait, double time_after_path_start = 0.0)
|
||||
: path_idx(path_idx)
|
||||
, time_after_path_start(time_after_path_start)
|
||||
, extruder(extruder)
|
||||
, temperature(temperature)
|
||||
, wait(wait)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* Write the temperature command at the current position in the gcode.
|
||||
* \param gcode The actual gcode writer
|
||||
*/
|
||||
void write(GCodeExport& gcode)
|
||||
{
|
||||
gcode.writeTemperatureCommand(extruder, temperature, wait);
|
||||
}
|
||||
};
|
||||
|
||||
class GCodePlanner; // forward declaration so that TimeMaterialEstimates can be a friend
|
||||
|
||||
/*!
|
||||
* Time and material estimates for a portion of paths, e.g. layer, extruder plan, path.
|
||||
*/
|
||||
class TimeMaterialEstimates
|
||||
{
|
||||
friend class GCodePlanner;
|
||||
private:
|
||||
double extrude_time; //!< in seconds
|
||||
double unretracted_travel_time; //!< in seconds
|
||||
double retracted_travel_time; //!< in seconds
|
||||
double material; //!< in mm^3
|
||||
public:
|
||||
|
||||
TimeMaterialEstimates(double extrude_time, double unretracted_travel_time, double retracted_travel_time, double material)
|
||||
: extrude_time(extrude_time)
|
||||
, unretracted_travel_time(unretracted_travel_time)
|
||||
, retracted_travel_time(retracted_travel_time)
|
||||
, material(material)
|
||||
{
|
||||
}
|
||||
TimeMaterialEstimates()
|
||||
: extrude_time(0.0)
|
||||
, unretracted_travel_time(0.0)
|
||||
, retracted_travel_time(0.0)
|
||||
, material(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set all estimates to zero.
|
||||
*/
|
||||
void reset()
|
||||
{
|
||||
extrude_time = 0.0;
|
||||
unretracted_travel_time = 0.0;
|
||||
retracted_travel_time = 0.0;
|
||||
material = 0.0;
|
||||
}
|
||||
|
||||
TimeMaterialEstimates operator+(const TimeMaterialEstimates& other)
|
||||
{
|
||||
return TimeMaterialEstimates(extrude_time+other.extrude_time, unretracted_travel_time+other.unretracted_travel_time, retracted_travel_time+other.retracted_travel_time, material+other.material);
|
||||
}
|
||||
|
||||
TimeMaterialEstimates& operator+=(const TimeMaterialEstimates& other)
|
||||
{
|
||||
extrude_time += other.extrude_time;
|
||||
unretracted_travel_time += other.unretracted_travel_time;
|
||||
retracted_travel_time += other.retracted_travel_time;
|
||||
material += other.material;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Subtracts the specified estimates from these estimates and returns
|
||||
* the result.
|
||||
*
|
||||
* Each of the estimates in this class are individually subtracted.
|
||||
*
|
||||
* \param other The estimates to subtract from these estimates.
|
||||
* \return These estimates with the specified estimates subtracted.
|
||||
*/
|
||||
TimeMaterialEstimates operator-(const TimeMaterialEstimates& other);
|
||||
|
||||
/*!
|
||||
* \brief Subtracts the specified elements from these estimates.
|
||||
*
|
||||
* This causes the estimates in this instance to change. Each of the
|
||||
* estimates in this class are individually subtracted.
|
||||
*
|
||||
* \param other The estimates to subtract from these estimates.
|
||||
* \return A reference to this instance.
|
||||
*/
|
||||
TimeMaterialEstimates& operator-=(const TimeMaterialEstimates& other);
|
||||
|
||||
double getTotalTime() const
|
||||
{
|
||||
return extrude_time + unretracted_travel_time + retracted_travel_time;
|
||||
}
|
||||
double getTotalUnretractedTime() const
|
||||
{
|
||||
return extrude_time + unretracted_travel_time;
|
||||
}
|
||||
double getTravelTime() const
|
||||
{
|
||||
return retracted_travel_time + unretracted_travel_time;
|
||||
}
|
||||
double getExtrudeTime() const
|
||||
{
|
||||
return extrude_time;
|
||||
}
|
||||
double getMaterial() const
|
||||
{
|
||||
return material;
|
||||
}
|
||||
};
|
||||
|
||||
class GCodePath
|
||||
{
|
||||
public:
|
||||
GCodePathConfig* config; //!< The configuration settings of the path.
|
||||
float flow; //!< A type-independent flow configuration (used for wall overlap compensation)
|
||||
bool retract; //!< Whether the path is a move path preceded by a retraction move; whether the path is a retracted move path.
|
||||
int extruder; //!< The extruder used for this path.
|
||||
std::vector<Point> points; //!< The points constituting this path.
|
||||
bool done;//!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
|
||||
|
||||
TimeMaterialEstimates estimates; //!< Naive time and material estimates
|
||||
|
||||
bool isTravelPath()
|
||||
{
|
||||
return config->isTravelPath();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Can only be called after the layer height has been set (which is done while writing the gcode!)
|
||||
*/
|
||||
double getExtrusionMM3perMM()
|
||||
{
|
||||
return flow * config->getExtrusionMM3perMM();
|
||||
}
|
||||
};
|
||||
|
||||
class ExtruderPlan
|
||||
{
|
||||
public:
|
||||
std::vector<GCodePath> paths;
|
||||
std::list<NozzleTempInsert> inserts;
|
||||
|
||||
int extruder; //!< The extruder used for this paths in the current plan.
|
||||
double required_temp;
|
||||
|
||||
TimeMaterialEstimates estimates;
|
||||
|
||||
ExtruderPlan(int extruder)
|
||||
: extruder(extruder)
|
||||
, required_temp(-1)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Add a new Insert, constructed with the given arguments
|
||||
*/
|
||||
template<typename... Args>
|
||||
void insertCommand(Args&&... contructor_args)
|
||||
{
|
||||
inserts.emplace_back(contructor_args...);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Insert the inserts into gcode which should be inserted before @p path_idx
|
||||
*/
|
||||
void handleInserts(unsigned int& path_idx, GCodeExport& gcode)
|
||||
{
|
||||
while ( ! inserts.empty() && path_idx >= inserts.front().path_idx)
|
||||
{ // handle the Insert to be inserted before this path_idx (and all inserts not handled yet)
|
||||
inserts.front().write(gcode);
|
||||
inserts.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Insert all remaining temp inserts into gcode, to be called at the end of an extruder plan
|
||||
*/
|
||||
void handleAllRemainingInserts(GCodeExport& gcode)
|
||||
{
|
||||
while ( ! inserts.empty() )
|
||||
{ // handle the Insert to be inserted before this path_idx (and all inserts not handled yet)
|
||||
NozzleTempInsert& insert = inserts.front();
|
||||
assert(insert.path_idx == paths.size());
|
||||
insert.write(gcode);
|
||||
inserts.pop_front();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class LayerPlanBuffer; // forward declaration to prevent circular dependency
|
||||
/*!
|
||||
* The GCodePlanner class stores multiple moves that are planned.
|
||||
* It facilitates the combing to keep the head inside the print.
|
||||
* It also keeps track of the print time estimate for this planning so speed adjustments can be made for the minimal-layer-time.
|
||||
*/
|
||||
class GCodePlanner
|
||||
class GCodePlanner : public NoCopy
|
||||
{
|
||||
friend class LayerPlanBuffer;
|
||||
private:
|
||||
GCodeExport& gcode;
|
||||
SliceDataStorage& storage;
|
||||
|
||||
Point lastPosition;
|
||||
std::vector<GCodePath> paths;
|
||||
CommandSocket* commandSocket;
|
||||
|
||||
bool was_combing;
|
||||
bool is_going_to_comb;
|
||||
int layer_nr;
|
||||
|
||||
int z;
|
||||
|
||||
int layer_thickness;
|
||||
|
||||
Point start_position;
|
||||
Point lastPosition;
|
||||
|
||||
std::vector<ExtruderPlan> extruder_plans; //!< should always contain at least one ExtruderPlan
|
||||
|
||||
bool was_inside; //!< Whether the last planned (extrusion) move was inside a layer part
|
||||
bool is_inside; //!< Whether the destination of the next planned travel move is inside a layer part
|
||||
Polygons comb_boundary_inside; //!< The boundary within which to comb, or to move into when performing a retraction.
|
||||
Comb* comb;
|
||||
|
||||
RetractionConfig* last_retraction_config;
|
||||
|
||||
GCodePathConfig travelConfig; //!< The config used for travel moves (only the speed and retraction config are set!)
|
||||
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings;
|
||||
|
||||
double extrudeSpeedFactor;
|
||||
double travelSpeedFactor; // TODO: remove this unused var?
|
||||
int currentExtruder;
|
||||
double travelSpeedFactor;
|
||||
|
||||
double fan_speed;
|
||||
|
||||
double extraTime;
|
||||
double totalPrintTime;
|
||||
@@ -82,26 +289,53 @@ private:
|
||||
*/
|
||||
void forceNewPathStart();
|
||||
public:
|
||||
/*
|
||||
/*!
|
||||
*
|
||||
* \param travel_avoid_other_parts Whether to avoid other layer parts when travaeling through air.
|
||||
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
|
||||
* \param last_position The position of the head at the start of this gcode layer
|
||||
*/
|
||||
GCodePlanner(GCodeExport& gcode, SliceDataStorage& storage, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
GCodePlanner(CommandSocket* commandSocket, SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
~GCodePlanner();
|
||||
|
||||
void setCombing(bool going_to_comb);
|
||||
private:
|
||||
/*!
|
||||
* Compute the boundary within which to comb, or to move into when performing a retraction.
|
||||
* \return the comb_boundary_inside
|
||||
*/
|
||||
Polygons computeCombBoundaryInside();
|
||||
|
||||
public:
|
||||
int getLayerNr()
|
||||
{
|
||||
return layer_nr;
|
||||
}
|
||||
|
||||
Point getLastPosition()
|
||||
{
|
||||
return lastPosition;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set whether the next destination is inside a layer part or not.
|
||||
*
|
||||
* Features like infill, walls, skin etc. are considered inside.
|
||||
* Features like prime tower and support are considered outside.
|
||||
*/
|
||||
void setIsInside(bool going_to_comb);
|
||||
|
||||
bool setExtruder(int extruder);
|
||||
|
||||
/*!
|
||||
* Get the last planned extruder.
|
||||
*/
|
||||
int getExtruder()
|
||||
{
|
||||
return currentExtruder;
|
||||
return extruder_plans.back().extruder;
|
||||
}
|
||||
|
||||
void setExtrudeSpeedFactor(double speedFactor)
|
||||
{
|
||||
if (speedFactor < 1) speedFactor = 1.0;
|
||||
this->extrudeSpeedFactor = speedFactor;
|
||||
}
|
||||
double getExtrudeSpeedFactor()
|
||||
@@ -117,16 +351,12 @@ public:
|
||||
{
|
||||
return this->travelSpeedFactor;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Whether the current retracted path is to be an extruder switch retraction.
|
||||
* This function is used to avoid a G10 S1 after a G10.
|
||||
*
|
||||
* \param path_idx The index of the current retracted path
|
||||
* \return Whether the path should be an extgruder switch retracted path
|
||||
*/
|
||||
bool makeRetractSwitchRetract(unsigned int path_idx);
|
||||
|
||||
|
||||
void setFanSpeed(double _fan_speed)
|
||||
{
|
||||
fan_speed = _fan_speed;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Add a travel path to a certain point, retract if needed and when avoiding boundary crossings:
|
||||
* avoiding obstacles and comb along the boundary of parts.
|
||||
@@ -159,15 +389,53 @@ public:
|
||||
*/
|
||||
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist = 0);
|
||||
|
||||
/*!
|
||||
* Compute naive time estimates (without accountign for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
|
||||
* and store them in each ExtruderPlan and each GCodePath.
|
||||
*
|
||||
* \return the total estimates of this layer
|
||||
*/
|
||||
TimeMaterialEstimates computeNaiveTimeEstimates();
|
||||
|
||||
void forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrusionTime);
|
||||
|
||||
void getNaiveTimeEstimates(double& travelTime, double& extrudeTime);
|
||||
|
||||
/*!
|
||||
* Write the planned paths to gcode
|
||||
*
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
*/
|
||||
void writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int layerThickness);
|
||||
|
||||
/*!
|
||||
* Complete all GcodePathConfig s by
|
||||
* - altering speed to conform to speed_layer_0
|
||||
* - setting the layer_height (and thereby computing the extrusionMM3perMM)
|
||||
*/
|
||||
void completeConfigs();
|
||||
|
||||
/*!
|
||||
* Interpolate between the initial layer speeds and the eventual speeds.
|
||||
*/
|
||||
void processInitialLayersSpeedup();
|
||||
|
||||
/*!
|
||||
* Whether the current retracted path is to be an extruder switch retraction.
|
||||
* This function is used to avoid a G10 S1 after a G10.
|
||||
*
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
* \param extruder_plan_idx The index of the current extruder plan
|
||||
* \param path_idx The index of the current retracted path
|
||||
* \return Whether the path should be an extgruder switch retracted path
|
||||
*/
|
||||
bool makeRetractSwitchRetract(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx);
|
||||
|
||||
/*!
|
||||
* Writes a path to GCode and performs coasting, or returns false if it did nothing.
|
||||
*
|
||||
* Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines.
|
||||
*
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
* \param extruder_plan_idx The index of the current extruder plan
|
||||
* \param path_idx The index into GCodePlanner::paths for the next path to be written to GCode.
|
||||
* \param layerThickness The height of the current layer.
|
||||
* \param coasting_volume_move The volume otherwise leaked during a normal move.
|
||||
@@ -178,7 +446,7 @@ public:
|
||||
* \param coasting_min_volume_retract The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_retract.
|
||||
* \return Whether any GCode has been written for the path.
|
||||
*/
|
||||
bool writePathWithCoasting(unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract);
|
||||
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract);
|
||||
|
||||
/*!
|
||||
* Writes a path to GCode and performs coasting, or returns false if it did nothing.
|
||||
@@ -187,6 +455,7 @@ public:
|
||||
*
|
||||
* Paths shorter than \p coasting_min_volume will use less \p coasting_volume linearly.
|
||||
*
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
* \param path The extrusion path to be written to GCode.
|
||||
* \param path_next The next travel path to be written to GCode.
|
||||
* \param layerThickness The height of the current layer.
|
||||
@@ -196,23 +465,36 @@ public:
|
||||
* \param extruder_switch_retract (optional) For a coasted path followed by a retraction: whether to retract normally, or do an extruder switch retraction.
|
||||
* \return Whether any GCode has been written for the path.
|
||||
*/
|
||||
bool writePathWithCoasting(GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract = false);
|
||||
bool writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract = false);
|
||||
|
||||
/*!
|
||||
* Write a retraction: either an extruder switch retraction or a normal retraction based on the last extrusion paths retraction config.
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
* \param extruder_plan_idx The index of the current extruder plan
|
||||
* \param path_idx_travel_after Index in GCodePlanner::paths to the travel move before which to do the retraction
|
||||
*/
|
||||
void writeRetraction(unsigned int path_idx_travel_after);
|
||||
void writeRetraction(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx_travel_after);
|
||||
|
||||
/*!
|
||||
* Write a retraction: either an extruder switch retraction or a normal retraction based on the given retraction config.
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
* \param extruder_switch_retract Whether to write an extruder switch retract
|
||||
* \param retraction_config The config used.
|
||||
*/
|
||||
void writeRetraction(bool extruder_switch_retract, RetractionConfig* retraction_config);
|
||||
void writeRetraction(GCodeExport& gcode, bool extruder_switch_retract, RetractionConfig* retraction_config);
|
||||
|
||||
void writeGCode(bool liftHeadIfNeeded, int layerThickness);
|
||||
void moveInsideCombBoundary(int arg1);
|
||||
/*!
|
||||
* Applying speed corrections for minimal layer times and determine the fanSpeed.
|
||||
*/
|
||||
void processFanSpeedAndMinimalLayerTime();
|
||||
|
||||
/*!
|
||||
* Add a travel move to the layer plan to move inside the current layer part by a given distance away from the outline.
|
||||
* This is supposed to be called when the nozzle is around the boundary of a layer part, not when the nozzle is in the middle of support, or in the middle of the air.
|
||||
*
|
||||
* \param distance The distance to the comb boundary after we moved inside it.
|
||||
*/
|
||||
void moveInsideCombBoundary(int distance);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+2
-69
@@ -138,24 +138,6 @@ void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, i
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* generate lines within the area of \p in_outline, at regular intervals of \p lineSpacing
|
||||
*
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
|
||||
*
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'.
|
||||
* Scansegment x is the area between scanline x and scanline x+1
|
||||
*
|
||||
* algorithm:
|
||||
* 1) for each line segment of each polygon:
|
||||
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
|
||||
* (zigzag): add boundary segments to result
|
||||
* 2) for each scanline:
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
*/
|
||||
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
|
||||
{
|
||||
if (lineSpacing == 0) return;
|
||||
@@ -220,55 +202,6 @@ void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extr
|
||||
else return generateZigZagIninfill_noEndPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation);
|
||||
}
|
||||
|
||||
/*!
|
||||
* adapted from generateLineInfill(.)
|
||||
*
|
||||
* generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside [in_outline]
|
||||
* sigzag:
|
||||
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
|
||||
*
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'
|
||||
*
|
||||
* algorithm:
|
||||
* 1. for each line segment of each polygon:
|
||||
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
|
||||
* (zigzag): add boundary segments to result
|
||||
* 2. for each scanline:
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
* zigzag algorithm:
|
||||
* while walking around (each) polygon (1.)
|
||||
* if polygon intersects with even scanline
|
||||
* start boundary segment (add each following segment to the [result])
|
||||
* when polygon intersects with a scanline again
|
||||
* stop boundary segment (stop adding segments to the [result])
|
||||
* if polygon intersects with even scanline again (instead of odd)
|
||||
* dont add the last line segment to the boundary (unless [connect_zigzags])
|
||||
*
|
||||
*
|
||||
* <--
|
||||
* ___
|
||||
* | | |
|
||||
* | | |
|
||||
* | |___|
|
||||
* -->
|
||||
*
|
||||
* ^ = even scanline
|
||||
*
|
||||
* start boundary from even scanline! :D
|
||||
*
|
||||
*
|
||||
* _____
|
||||
* | | | ,
|
||||
* | | | |
|
||||
* |_____| |__/
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
* ^ disconnected end piece
|
||||
*/
|
||||
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags)
|
||||
{
|
||||
// if (in_outline.size() == 0) return;
|
||||
@@ -303,7 +236,7 @@ void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& resu
|
||||
std::vector<Point> unevenBoundarySegment; // stored cause for connected_zigzags a boundary segment which ends in an uneven scanline needs to be included
|
||||
|
||||
bool isFirstBoundarySegment = true;
|
||||
bool firstBoundarySegmentEndsInEven;
|
||||
bool firstBoundarySegmentEndsInEven = false;
|
||||
|
||||
bool isEvenScanSegment = false;
|
||||
|
||||
@@ -435,7 +368,7 @@ void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& re
|
||||
std::vector<Point> boundarySegment;
|
||||
|
||||
bool isFirstBoundarySegment = true;
|
||||
bool firstBoundarySegmentEndsInEven;
|
||||
bool firstBoundarySegmentEndsInEven = true;
|
||||
|
||||
bool isEvenScanSegment = false;
|
||||
|
||||
|
||||
+114
-39
@@ -5,47 +5,122 @@
|
||||
#include "utils/polygon.h"
|
||||
#include "settings.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
class Infill
|
||||
namespace cura
|
||||
{
|
||||
EFillMethod pattern;
|
||||
const Polygons& in_outline;
|
||||
int outlineOffset;
|
||||
bool avoidOverlappingPerimeters;
|
||||
int extrusion_width;
|
||||
int line_distance;
|
||||
double infill_overlap;
|
||||
double fill_angle;
|
||||
bool connect_zigzags;
|
||||
bool use_endPieces;
|
||||
|
||||
public:
|
||||
Infill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces)
|
||||
: pattern(pattern)
|
||||
, in_outline(in_outline)
|
||||
, outlineOffset(outlineOffset)
|
||||
, avoidOverlappingPerimeters(avoidOverlappingPerimeters)
|
||||
, extrusion_width(extrusion_width)
|
||||
, line_distance(line_distance)
|
||||
, infill_overlap(infill_overlap)
|
||||
, fill_angle(fill_angle)
|
||||
, connect_zigzags(connect_zigzags)
|
||||
, use_endPieces(use_endPieces)
|
||||
class Infill
|
||||
{
|
||||
}
|
||||
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
|
||||
};
|
||||
|
||||
void generateInfill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, Polygons& result_polygons, Polygons& result_lines, Polygons* in_between, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces);
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
|
||||
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters);
|
||||
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces);
|
||||
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags);
|
||||
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
EFillMethod pattern;
|
||||
const Polygons& in_outline;
|
||||
int outlineOffset;
|
||||
bool avoidOverlappingPerimeters;
|
||||
int extrusion_width;
|
||||
int line_distance;
|
||||
double infill_overlap;
|
||||
double fill_angle;
|
||||
bool connect_zigzags;
|
||||
bool use_endPieces;
|
||||
|
||||
public:
|
||||
Infill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces)
|
||||
: pattern(pattern)
|
||||
, in_outline(in_outline)
|
||||
, outlineOffset(outlineOffset)
|
||||
, avoidOverlappingPerimeters(avoidOverlappingPerimeters)
|
||||
, extrusion_width(extrusion_width)
|
||||
, line_distance(line_distance)
|
||||
, infill_overlap(infill_overlap)
|
||||
, fill_angle(fill_angle)
|
||||
, connect_zigzags(connect_zigzags)
|
||||
, use_endPieces(use_endPieces)
|
||||
{
|
||||
}
|
||||
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
|
||||
};
|
||||
|
||||
void generateInfill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, Polygons& result_polygons, Polygons& result_lines, Polygons* in_between, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces);
|
||||
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
|
||||
|
||||
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters);
|
||||
|
||||
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
|
||||
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
|
||||
/*!
|
||||
* generate lines within the area of \p in_outline, at regular intervals of \p lineSpacing
|
||||
*
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
|
||||
*
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'.
|
||||
* Scansegment x is the area between scanline x and scanline x+1
|
||||
*
|
||||
* algorithm:
|
||||
* 1) for each line segment of each polygon:
|
||||
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
|
||||
* (zigzag): add boundary segments to result
|
||||
* 2) for each scanline:
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
*/
|
||||
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
|
||||
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces);
|
||||
|
||||
/*!
|
||||
* adapted from generateLineInfill(.)
|
||||
*
|
||||
* generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside [in_outline]
|
||||
* sigzag:
|
||||
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
|
||||
*
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'
|
||||
*
|
||||
* algorithm:
|
||||
* 1. for each line segment of each polygon:
|
||||
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
|
||||
* (zigzag): add boundary segments to result
|
||||
* 2. for each scanline:
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
* zigzag algorithm:
|
||||
* while walking around (each) polygon (1.)
|
||||
* if polygon intersects with even scanline
|
||||
* start boundary segment (add each following segment to the [result])
|
||||
* when polygon intersects with a scanline again
|
||||
* stop boundary segment (stop adding segments to the [result])
|
||||
* if polygon intersects with even scanline again (instead of odd)
|
||||
* dont add the last line segment to the boundary (unless [connect_zigzags])
|
||||
*
|
||||
*
|
||||
* <--
|
||||
* ___
|
||||
* | | |
|
||||
* | | |
|
||||
* | |___|
|
||||
* -->
|
||||
*
|
||||
* ^ = even scanline
|
||||
*
|
||||
* start boundary from even scanline! :D
|
||||
*
|
||||
*
|
||||
* _____
|
||||
* | | | ,
|
||||
* | | | |
|
||||
* |_____| |__/
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
* ^ disconnected end piece
|
||||
*/
|
||||
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags);
|
||||
|
||||
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
}//namespace cura
|
||||
|
||||
#endif//INFILL_H
|
||||
|
||||
@@ -71,4 +71,33 @@ void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int l
|
||||
}
|
||||
}
|
||||
|
||||
void generateWallReinforcementWallExtraWalls(SliceLayerPart* part, ReinforcementWall& reinforcement_wall, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
// optimize all the polygons. Every point removed saves time in the long run.
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls[0].simplify();
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls[0].size() < 1)
|
||||
{
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls.pop_back();
|
||||
}
|
||||
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls[0].size() > 0)
|
||||
{
|
||||
for(int i=1; i<insetCount; i++)
|
||||
{
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls.push_back(Polygons());
|
||||
PolygonUtils::offsetExtrusionWidth(reinforcement_wall.wall_reinforcement_axtra_walls[i-1], true, line_width_x, reinforcement_wall.wall_reinforcement_axtra_walls[i], &part->perimeterGaps, avoidOverlappingPerimeters);
|
||||
|
||||
|
||||
//Finally optimize all the polygons. Every point removed saves time in the long run.
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls[i].simplify();
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls[i].size() < 1)
|
||||
{
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls.pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -36,6 +36,16 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
|
||||
*/
|
||||
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
|
||||
/*!
|
||||
* Generates the wall reinforcement extra walls for a single layer part.
|
||||
*
|
||||
* \param part The part for which to generate the extra walls.
|
||||
* \param line_width_x line width of the walls
|
||||
* \param insetCount The number of insets to to generate
|
||||
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
|
||||
*/
|
||||
void generateWallReinforcementWallExtraWalls(SliceLayerPart* part, ReinforcementWall& reinforcement_wall, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//INSET_H
|
||||
|
||||
+24
-17
@@ -116,11 +116,11 @@ void slice(int argc, char **argv)
|
||||
|
||||
FMatrix3x3 transformation; // the transformation applied to a model when loaded
|
||||
|
||||
MeshGroup meshgroup(FffProcessor::getInstance());
|
||||
MeshGroup* meshgroup = new MeshGroup(FffProcessor::getInstance());
|
||||
|
||||
int extruder_train_nr = 0;
|
||||
|
||||
SettingsBase* last_extruder_train = meshgroup.getExtruderTrain(0);
|
||||
SettingsBase* last_extruder_train = meshgroup->createExtruderTrain(0);
|
||||
SettingsBase* last_settings_object = FffProcessor::getInstance();
|
||||
for(int argn = 2; argn < argc; argn++)
|
||||
{
|
||||
@@ -134,20 +134,21 @@ void slice(int argc, char **argv)
|
||||
try {
|
||||
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
|
||||
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
|
||||
meshgroup.finalize();
|
||||
meshgroup->finalize();
|
||||
log("Loaded from disk in %5.3fs\n", FffProcessor::getInstance()->time_keeper.restart());
|
||||
|
||||
for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++)
|
||||
{ // initialize remaining extruder trains and load the defaults
|
||||
meshgroup.getExtruderTrain(extruder_nr)->setExtruderTrainDefaults(extruder_nr); // also initializes yet uninitialized extruder trains
|
||||
meshgroup->getExtruderTrain(extruder_nr)->setExtruderTrainDefaults(extruder_nr); // create new extruder train objects or use already existing ones
|
||||
}
|
||||
//start slicing
|
||||
FffProcessor::getInstance()->processMeshGroup(&meshgroup);
|
||||
FffProcessor::getInstance()->processMeshGroup(meshgroup);
|
||||
|
||||
// initialize loading of new meshes
|
||||
FffProcessor::getInstance()->time_keeper.restart();
|
||||
meshgroup = MeshGroup(FffProcessor::getInstance());
|
||||
last_settings_object = &meshgroup;
|
||||
delete meshgroup;
|
||||
meshgroup = new MeshGroup(FffProcessor::getInstance());
|
||||
last_settings_object = meshgroup;
|
||||
}catch(...){
|
||||
cura::logError("Unknown exception\n");
|
||||
exit(1);
|
||||
@@ -177,8 +178,8 @@ void slice(int argc, char **argv)
|
||||
case 'e':
|
||||
str++;
|
||||
extruder_train_nr = int(*str - '0'); // TODO: parse int instead (now "-e10"="-e:" , "-e11"="-e;" , "-e12"="-e<" .. etc)
|
||||
last_settings_object = meshgroup.getExtruderTrain(extruder_train_nr);
|
||||
last_extruder_train = meshgroup.getExtruderTrain(extruder_train_nr);
|
||||
last_settings_object = meshgroup->createExtruderTrain(extruder_train_nr);
|
||||
last_extruder_train = last_settings_object;
|
||||
break;
|
||||
case 'l':
|
||||
argn++;
|
||||
@@ -186,13 +187,13 @@ void slice(int argc, char **argv)
|
||||
log("Loading %s from disk...\n", argv[argn]);
|
||||
// transformation = // TODO: get a transformation from somewhere
|
||||
|
||||
if (!loadMeshIntoMeshGroup(&meshgroup, argv[argn], transformation, last_extruder_train))
|
||||
if (!loadMeshIntoMeshGroup(meshgroup, argv[argn], transformation, last_extruder_train))
|
||||
{
|
||||
logError("Failed to load model: %s\n", argv[argn]);
|
||||
}
|
||||
else
|
||||
{
|
||||
last_settings_object = &(meshgroup.meshes.back()); // pointer is valid until a new object is added, so this is OK
|
||||
last_settings_object = &(meshgroup->meshes.back()); // pointer is valid until a new object is added, so this is OK
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
@@ -204,7 +205,7 @@ void slice(int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
last_settings_object = &meshgroup;
|
||||
last_settings_object = meshgroup;
|
||||
case 's':
|
||||
{
|
||||
//Parse the given setting and store it.
|
||||
@@ -240,7 +241,7 @@ void slice(int argc, char **argv)
|
||||
|
||||
for (extruder_train_nr = 0; extruder_train_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_train_nr++)
|
||||
{ // initialize remaining extruder trains and load the defaults
|
||||
meshgroup.getExtruderTrain(extruder_train_nr)->setExtruderTrainDefaults(extruder_train_nr); // also initializes yet uninitialized extruder trains
|
||||
meshgroup->createExtruderTrain(extruder_train_nr)->setExtruderTrainDefaults(extruder_train_nr); // create new extruder train objects or use already existing ones
|
||||
}
|
||||
|
||||
|
||||
@@ -249,12 +250,12 @@ void slice(int argc, char **argv)
|
||||
#endif
|
||||
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
|
||||
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
|
||||
meshgroup.finalize();
|
||||
meshgroup->finalize();
|
||||
log("Loaded from disk in %5.3fs\n", FffProcessor::getInstance()->time_keeper.restart());
|
||||
|
||||
//start slicing
|
||||
FffProcessor::getInstance()->processMeshGroup(&meshgroup);
|
||||
|
||||
FffProcessor::getInstance()->processMeshGroup(meshgroup);
|
||||
|
||||
#ifndef DEBUG
|
||||
}catch(...){
|
||||
cura::logError("Unknown exception\n");
|
||||
@@ -263,7 +264,8 @@ void slice(int argc, char **argv)
|
||||
#endif
|
||||
//Finalize the processor, this adds the end.gcode. And reports statistics.
|
||||
FffProcessor::getInstance()->finalize();
|
||||
|
||||
|
||||
delete meshgroup;
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
@@ -317,6 +319,11 @@ int main(int argc, char **argv)
|
||||
{
|
||||
slice(argc, argv);
|
||||
}
|
||||
else if (stringcasecompare(argv[1], "help") == 0)
|
||||
{
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
cura::logError("Unknown command: %s\n", argv[1]);
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ void generateRaft(SliceDataStorage& storage, int distance)
|
||||
}
|
||||
else
|
||||
{
|
||||
storage.raftOutline = storage.getLayerOutlines(0, true);
|
||||
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+126
-94
@@ -4,6 +4,7 @@
|
||||
#include <iostream> // debug IO
|
||||
#include <libgen.h> // dirname
|
||||
#include <string>
|
||||
#include <algorithm> // find_if
|
||||
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
@@ -53,6 +54,16 @@ SettingConfig::SettingConfig(std::string key, std::string label, SettingContaine
|
||||
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
|
||||
}
|
||||
|
||||
void SettingContainer::debugOutputAllSettings()
|
||||
{
|
||||
std::cerr << "CATEGORY: " << key << std::endl;
|
||||
for (SettingConfig& child : children)
|
||||
{
|
||||
child.debugOutputAllSettings();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SettingRegistry::settingExists(std::string key) const
|
||||
{
|
||||
return settings.find(key) != settings.end();
|
||||
@@ -184,120 +195,64 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!category_iterator->value.HasMember("label") || !category_iterator->value["label"].IsString())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!category_iterator->value.HasMember("settings") || !category_iterator->value["settings"].IsObject())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
categories.emplace_back(category_iterator->name.GetString(), category_iterator->value["label"].GetString());
|
||||
SettingContainer* category = &categories.back();
|
||||
|
||||
const rapidjson::Value& json_object_container = category_iterator->value["settings"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
std::string cat_name = category_iterator->name.GetString();
|
||||
std::list<SettingContainer>::iterator category_found = std::find_if(categories.begin(), categories.end(), [&cat_name](SettingContainer& cat) { return cat.getKey().compare(cat_name) == 0; });
|
||||
if (category_found != categories.end())
|
||||
{ // category is already present; add settings to category
|
||||
SettingContainer* category = &*category_found;
|
||||
|
||||
const rapidjson::Value& json_object_container = category_iterator->value["settings"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(category, setting_iterator, warn_duplicates);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_addSettingToContainer(category, setting_iterator, warn_duplicates);
|
||||
if (!category_iterator->value.HasMember("label") || !category_iterator->value["label"].IsString())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
categories.emplace_back(cat_name, category_iterator->value["label"].GetString());
|
||||
SettingContainer* category = &categories.back();
|
||||
|
||||
const rapidjson::Value& json_object_container = category_iterator->value["settings"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(category, setting_iterator, warn_duplicates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (false && json_document.HasMember("overrides"))
|
||||
if (json_document.HasMember("overrides"))
|
||||
{
|
||||
const rapidjson::Value& json_object_container = json_document["overrides"];
|
||||
for (rapidjson::Value::ConstMemberIterator override_iterator = json_object_container.MemberBegin(); override_iterator != json_object_container.MemberEnd(); ++override_iterator)
|
||||
{
|
||||
SettingConfig* conf = getSettingConfig(override_iterator->name.GetString());
|
||||
_addSettingToContainer(conf, override_iterator, false);
|
||||
std::string setting = override_iterator->name.GetString();
|
||||
SettingConfig* conf = getSettingConfig(setting);
|
||||
_loadSettingValues(conf, override_iterator, false);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
|
||||
{
|
||||
const rapidjson::Value& data = json_object_it->value;
|
||||
|
||||
if (data.HasMember("type") && data["type"].IsString() &&
|
||||
(data["type"].GetString() == std::string("polygon") || data["type"].GetString() == std::string("polygons")))
|
||||
{
|
||||
logWarning("Loading polygon setting %s not implemented...\n", json_object_it->name.GetString());
|
||||
/// When this setting has children, add those children to the parent setting.
|
||||
if (data.HasMember("children") && data["children"].IsObject())
|
||||
{
|
||||
const rapidjson::Value& json_object_container = data["children"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(parent, setting_iterator, warn_duplicates, add_to_settings);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::string label;
|
||||
if (!json_object_it->value.HasMember("label") || !data["label"].IsString())
|
||||
{
|
||||
label = "N/A";
|
||||
}
|
||||
else
|
||||
{
|
||||
label = data["label"].GetString();
|
||||
}
|
||||
const rapidjson::Value& data = json_object_it->value;
|
||||
|
||||
/// Create the new setting config object.
|
||||
SettingConfig* config = parent->addChild(json_object_it->name.GetString(), label);
|
||||
|
||||
|
||||
/// Fill the setting config object with data we have in the json file.
|
||||
if (data.HasMember("type") && data["type"].IsString())
|
||||
{
|
||||
config->setType(data["type"].GetString());
|
||||
}
|
||||
if (data.HasMember("default"))
|
||||
{
|
||||
const rapidjson::Value& dflt = data["default"];
|
||||
if (dflt.IsString())
|
||||
{
|
||||
config->setDefault(dflt.GetString());
|
||||
}
|
||||
else if (dflt.IsTrue())
|
||||
{
|
||||
config->setDefault("true");
|
||||
}
|
||||
else if (dflt.IsFalse())
|
||||
{
|
||||
config->setDefault("false");
|
||||
}
|
||||
else if (dflt.IsNumber())
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << dflt.GetDouble();
|
||||
config->setDefault(ss.str());
|
||||
} // arrays are ignored because machine_extruder_trains needs to be handled separately
|
||||
else
|
||||
{
|
||||
logError("Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
|
||||
}
|
||||
}
|
||||
if (data.HasMember("unit") && data["unit"].IsString())
|
||||
{
|
||||
config->setUnit(data["unit"].GetString());
|
||||
}
|
||||
|
||||
/// Register the setting in the settings map lookup.
|
||||
if (warn_duplicates && settingExists(config->getKey()))
|
||||
{
|
||||
cura::logError("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", config->getKey().c_str(), config->getLabel().c_str(), getSettingConfig(config->getKey())->getLabel().c_str());
|
||||
}
|
||||
|
||||
if (add_to_settings)
|
||||
{
|
||||
settings[config->getKey()] = config;
|
||||
}
|
||||
|
||||
/// When this setting has children, add those children to this setting.
|
||||
if (data.HasMember("type") && data["type"].IsString() &&
|
||||
(data["type"].GetString() == std::string("polygon") || data["type"].GetString() == std::string("polygons")))
|
||||
{
|
||||
logWarning("Loading polygon setting %s not implemented...\n", json_object_it->name.GetString());
|
||||
/// When this setting has children, add those children to the parent setting.
|
||||
if (data.HasMember("children") && data["children"].IsObject())
|
||||
{
|
||||
const rapidjson::Value& json_object_container = data["children"];
|
||||
@@ -306,7 +261,84 @@ void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson
|
||||
_addSettingToContainer(parent, setting_iterator, warn_duplicates, add_to_settings);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::string label;
|
||||
if (!json_object_it->value.HasMember("label") || !data["label"].IsString())
|
||||
{
|
||||
label = "N/A";
|
||||
}
|
||||
else
|
||||
{
|
||||
label = data["label"].GetString();
|
||||
}
|
||||
|
||||
/// Create the new setting config object.
|
||||
SettingConfig* config = parent->addChild(json_object_it->name.GetString(), label);
|
||||
|
||||
_loadSettingValues(config, json_object_it, warn_duplicates, add_to_settings);
|
||||
}
|
||||
|
||||
void SettingRegistry::_loadSettingValues(SettingConfig* config, rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
|
||||
{
|
||||
const rapidjson::Value& data = json_object_it->value;
|
||||
/// Fill the setting config object with data we have in the json file.
|
||||
if (data.HasMember("type") && data["type"].IsString())
|
||||
{
|
||||
config->setType(data["type"].GetString());
|
||||
}
|
||||
if (data.HasMember("default"))
|
||||
{
|
||||
const rapidjson::Value& dflt = data["default"];
|
||||
if (dflt.IsString())
|
||||
{
|
||||
config->setDefault(dflt.GetString());
|
||||
}
|
||||
else if (dflt.IsTrue())
|
||||
{
|
||||
config->setDefault("true");
|
||||
}
|
||||
else if (dflt.IsFalse())
|
||||
{
|
||||
config->setDefault("false");
|
||||
}
|
||||
else if (dflt.IsNumber())
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << dflt.GetDouble();
|
||||
config->setDefault(ss.str());
|
||||
} // arrays are ignored because machine_extruder_trains needs to be handled separately
|
||||
else
|
||||
{
|
||||
logError("Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
|
||||
}
|
||||
}
|
||||
if (data.HasMember("unit") && data["unit"].IsString())
|
||||
{
|
||||
config->setUnit(data["unit"].GetString());
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
/// Register the setting in the settings map lookup.
|
||||
if (warn_duplicates && settingExists(config->getKey()))
|
||||
{
|
||||
cura::logError("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", config->getKey().c_str(), config->getLabel().c_str(), getSettingConfig(config->getKey())->getLabel().c_str());
|
||||
}
|
||||
|
||||
if (add_to_settings)
|
||||
{
|
||||
settings[config->getKey()] = config;
|
||||
}
|
||||
|
||||
/// When this setting has children, add those children to this setting.
|
||||
if (data.HasMember("children") && data["children"].IsObject())
|
||||
{
|
||||
const rapidjson::Value& json_object_container = data["children"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(config, setting_iterator, warn_duplicates, add_to_settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+35
-1
@@ -5,6 +5,7 @@
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <iostream> // debug out
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "rapidjson/document.h"
|
||||
@@ -52,6 +53,8 @@ public:
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void debugOutputAllSettings();
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -112,6 +115,15 @@ public:
|
||||
{
|
||||
return unit;
|
||||
}
|
||||
|
||||
void debugOutputAllSettings()
|
||||
{
|
||||
std::cerr << key << std::endl;
|
||||
for (SettingConfig& child : children)
|
||||
{
|
||||
child.debugOutputAllSettings();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -160,9 +172,21 @@ public:
|
||||
* \return an error code or zero of succeeded
|
||||
*/
|
||||
int loadJSONsettings(std::string filename);
|
||||
void debugOutputAllSettings()
|
||||
{
|
||||
for (SettingContainer& cat : categories)
|
||||
{
|
||||
cat.debugOutputAllSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string toString(rapidjson::Type type);
|
||||
|
||||
/*!
|
||||
* \param type type to convert to string
|
||||
* \return human readable version of json type
|
||||
*/
|
||||
static std::string toString(rapidjson::Type type);
|
||||
/*!
|
||||
* Load a json document.
|
||||
*
|
||||
@@ -181,10 +205,20 @@ private:
|
||||
*/
|
||||
int loadJSONsettingsFromDoc(rapidjson::Document& json_document, bool warn_duplicates);
|
||||
|
||||
/*!
|
||||
* Get the string from a json value (generally the default value field of a setting)
|
||||
* \param dflt The value to convert to string
|
||||
* \param setting_name The name of the setting (in case we need to display an error message)
|
||||
* \return The string
|
||||
*/
|
||||
static std::string toString(const rapidjson::Value& dflt, std::string setting_name = "?");
|
||||
|
||||
/*!
|
||||
* \param warn_duplicates whether to warn for duplicate definitions
|
||||
*/
|
||||
void _addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
|
||||
|
||||
void _loadSettingValues(SettingConfig* config, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+98
-3
@@ -14,6 +14,26 @@ namespace cura
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
std::string toString(EGCodeFlavor flavor)
|
||||
{
|
||||
switch (flavor)
|
||||
{
|
||||
case EGCodeFlavor::BFB:
|
||||
return "BFB";
|
||||
case EGCodeFlavor::MACH3:
|
||||
return "Mach3";
|
||||
case EGCodeFlavor::MAKERBOT:
|
||||
return "Makerbot";
|
||||
case EGCodeFlavor::ULTIGCODE:
|
||||
return "UltiGCode";
|
||||
case EGCodeFlavor::REPRAP_VOLUMATRIC:
|
||||
return "RepRap(Volumetric)";
|
||||
case EGCodeFlavor::REPRAP:
|
||||
default:
|
||||
return "RepRap";
|
||||
}
|
||||
}
|
||||
|
||||
SettingsBaseVirtual::SettingsBaseVirtual()
|
||||
: parent(NULL)
|
||||
{
|
||||
@@ -92,7 +112,7 @@ void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
|
||||
|
||||
if (!machine_extruder_trains)
|
||||
{
|
||||
logWarning("Error: no machine_extruder_trains category found in JSON!\n");
|
||||
// no machine_extruder_trains setting present; just use defaults for each train..
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,7 +120,7 @@ void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
|
||||
|
||||
if (!train)
|
||||
{
|
||||
logError("Not enough extruder trains specified in JSON: %i\n", extruder_nr);
|
||||
// not enough machine_extruder_trains settings present; just use defaults for this train..
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -180,6 +200,64 @@ double SettingsBaseVirtual::getSettingInSeconds(std::string key)
|
||||
return std::max(0.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key)
|
||||
{
|
||||
FlowTempGraph ret;
|
||||
const char* c_str = getSettingString(key).c_str();
|
||||
char const* char_p = c_str;
|
||||
while (*char_p != '[')
|
||||
{
|
||||
if (*char_p == '\0') //We've reached the end of string without encountering the first opening bracket.
|
||||
{
|
||||
return ret; //Empty at this point.
|
||||
}
|
||||
char_p++;
|
||||
}
|
||||
char_p++; // skip the '['
|
||||
for (; *char_p != '\0'; char_p++)
|
||||
{
|
||||
while (*char_p != '[')
|
||||
{
|
||||
if (*char_p == '\0') //We've reached the end of string without finding the next opening bracket.
|
||||
{
|
||||
return ret; //Don't continue parsing this item then. Just stop and return.
|
||||
}
|
||||
char_p++;
|
||||
}
|
||||
char_p++; // skip the '['
|
||||
char* end;
|
||||
double first = strtod(char_p, &end); //If not a valid number, this becomes zero.
|
||||
char_p = end;
|
||||
while (*char_p != ',')
|
||||
{
|
||||
if (*char_p == '\0') //We've reached the end of string without finding the comma.
|
||||
{
|
||||
return ret; //This entry is incomplete.
|
||||
}
|
||||
char_p++;
|
||||
}
|
||||
char_p++; // skip the ','
|
||||
double second = strtod(char_p, &end); //If not a valid number, this becomes zero.
|
||||
ret.data.emplace_back(first, second);
|
||||
char_p = end;
|
||||
while (*char_p != ']')
|
||||
{
|
||||
if (*char_p == '\0') //We've reached the end of string without finding the closing bracket.
|
||||
{
|
||||
return ret; //This entry is probably complete and has been added, but stop searching.
|
||||
}
|
||||
char_p++;
|
||||
}
|
||||
char_p++; // skip the ']'
|
||||
if (*char_p == ']' || *char_p == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
@@ -229,7 +307,7 @@ ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key)
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "everywhere")
|
||||
return ESupportType::EVERYWHERE;
|
||||
if (value == "touching_buildplate")
|
||||
if (value == "buildplate")
|
||||
return ESupportType::PLATFORM_ONLY;
|
||||
return ESupportType::NONE;
|
||||
}
|
||||
@@ -258,5 +336,22 @@ ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key)
|
||||
return ESurfaceMode::NORMAL;
|
||||
}
|
||||
|
||||
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "nowhere")
|
||||
{
|
||||
return FillPerimeterGapMode::NOWHERE;
|
||||
}
|
||||
if (value == "everywhere")
|
||||
{
|
||||
return FillPerimeterGapMode::EVERYWHERE;
|
||||
}
|
||||
if (value == "skin")
|
||||
{
|
||||
return FillPerimeterGapMode::SKIN;
|
||||
}
|
||||
return FillPerimeterGapMode::NOWHERE;
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "utils/floatpoint.h"
|
||||
|
||||
#include "FlowTempGraph.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
@@ -75,6 +77,11 @@ enum class EGCodeFlavor
|
||||
REPRAP_VOLUMATRIC = 5,
|
||||
};
|
||||
|
||||
/*!
|
||||
* Converts a gcode flavor type to string so that it can be included in the gcode.
|
||||
*/
|
||||
std::string toString(EGCodeFlavor flavor);
|
||||
|
||||
/*!
|
||||
* In Cura different infill methods are available.
|
||||
* This enum defines which fill patterns are available to get a uniform naming troughout the engine.
|
||||
@@ -124,6 +131,13 @@ enum class ESurfaceMode
|
||||
BOTH
|
||||
};
|
||||
|
||||
enum class FillPerimeterGapMode
|
||||
{
|
||||
NOWHERE,
|
||||
EVERYWHERE,
|
||||
SKIN
|
||||
};
|
||||
|
||||
#define MAX_EXTRUDERS 16
|
||||
|
||||
//Maximum number of infill layers that can be combined into a single infill extrusion area.
|
||||
@@ -165,12 +179,17 @@ public:
|
||||
double getSettingInPercentage(std::string key);
|
||||
double getSettingInSeconds(std::string key);
|
||||
|
||||
FlowTempGraph getSettingAsFlowTempGraph(std::string key);
|
||||
|
||||
std::vector<std::pair<double, double>> getSettingAsPointVector(std::string key);
|
||||
|
||||
EGCodeFlavor getSettingAsGCodeFlavor(std::string key);
|
||||
EFillMethod getSettingAsFillMethod(std::string key);
|
||||
EPlatformAdhesion getSettingAsPlatformAdhesion(std::string key);
|
||||
ESupportType getSettingAsSupportType(std::string key);
|
||||
EZSeamType getSettingAsZSeamType(std::string key);
|
||||
ESurfaceMode getSettingAsSurfaceMode(std::string key);
|
||||
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key);
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
+104
-42
@@ -2,15 +2,15 @@
|
||||
#include "skin.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
|
||||
#define MIN_AREA_SIZE (INT2MM(extrusionWidth) * INT2MM(extrusionWidth))
|
||||
#define MIN_AREA_SIZE (0.4 * 0.4)
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
generateSkinAreas(layerNr, storage, extrusionWidth, downSkinCount, upSkinCount);
|
||||
generateSkinAreas(layerNr, storage, innermost_wall_extrusion_width, downSkinCount, upSkinCount, wall_line_count, no_small_gaps_heuristic);
|
||||
|
||||
SliceLayer* layer = &storage.layers[layerNr];
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
@@ -20,39 +20,76 @@ void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, i
|
||||
}
|
||||
}
|
||||
|
||||
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
|
||||
void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic)
|
||||
{
|
||||
SliceLayer& layer = storage.layers[layerNr];
|
||||
|
||||
SliceLayer& layer = storage.layers[layer_nr];
|
||||
|
||||
if (downSkinCount == 0 && upSkinCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
|
||||
{
|
||||
SliceLayerPart& part = layer.parts[partNr];
|
||||
|
||||
Polygons upskin = part.insets.back().offset(-extrusionWidth/2);
|
||||
Polygons downskin = upskin;
|
||||
|
||||
|
||||
if (static_cast<int>(layerNr - downSkinCount) >= 0)
|
||||
if (int(part.insets.size()) < wall_line_count)
|
||||
{
|
||||
SliceLayer& layer2 = storage.layers[layerNr - downSkinCount];
|
||||
for(SliceLayerPart& part2 : layer2.parts)
|
||||
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no skin.
|
||||
}
|
||||
|
||||
Polygons upskin = part.insets.back().offset(-innermost_wall_extrusion_width/2);
|
||||
Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
|
||||
if (upSkinCount == 0) upskin = Polygons();
|
||||
|
||||
auto getInsidePolygons = [&part](SliceLayer& layer2)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
downskin = downskin.difference(part2.insets.back());
|
||||
Polygons result;
|
||||
for(SliceLayerPart& part2 : layer2.parts)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
result.add(part2.insets.back());
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
if (no_small_gaps_heuristic)
|
||||
{
|
||||
if (static_cast<int>(layer_nr - downSkinCount) >= 0)
|
||||
{
|
||||
downskin = downskin.difference(getInsidePolygons(storage.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
|
||||
}
|
||||
|
||||
if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(storage.layers.size()))
|
||||
{
|
||||
upskin = upskin.difference(getInsidePolygons(storage.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
|
||||
}
|
||||
}
|
||||
if (static_cast<int>(layerNr + upSkinCount) < static_cast<int>(storage.layers.size()))
|
||||
else
|
||||
{
|
||||
SliceLayer& layer2 = storage.layers[layerNr + upSkinCount];
|
||||
for(SliceLayerPart& part2 : layer2.parts)
|
||||
if (layer_nr > 0 && downSkinCount > 0)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
upskin = upskin.difference(part2.insets.back());
|
||||
Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]);
|
||||
for (int downskin_layer_nr = std::max(0, layer_nr - downSkinCount); downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
|
||||
{
|
||||
not_air = not_air.intersection(getInsidePolygons(storage.layers[downskin_layer_nr]));
|
||||
}
|
||||
downskin = downskin.difference(not_air); // skin overlaps with the walls
|
||||
}
|
||||
|
||||
if (layer_nr < static_cast<int>(storage.layers.size()) - 1 && upSkinCount > 0)
|
||||
{
|
||||
Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]);
|
||||
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < std::min(static_cast<int>(storage.layers.size()) - 1, layer_nr + upSkinCount); upskin_layer_nr++)
|
||||
{
|
||||
not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
|
||||
}
|
||||
upskin = upskin.difference(not_air); // skin overlaps with the walls
|
||||
}
|
||||
}
|
||||
|
||||
Polygons skin = upskin.unionPolygons(downskin);
|
||||
|
||||
|
||||
skin.removeSmallAreas(MIN_AREA_SIZE);
|
||||
|
||||
for (PolygonsPart& skin_area_part : skin.splitIntoParts())
|
||||
@@ -97,12 +134,16 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
|
||||
}
|
||||
}
|
||||
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap)
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap, int wall_line_count)
|
||||
{
|
||||
SliceLayer& layer = storage.layers[layerNr];
|
||||
|
||||
for(SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
if (int(part.insets.size()) < wall_line_count)
|
||||
{
|
||||
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
|
||||
}
|
||||
Polygons infill = part.insets.back().offset(-extrusionWidth / 2 - infill_skin_overlap);
|
||||
|
||||
for(SliceLayerPart& part2 : layer.parts)
|
||||
@@ -121,31 +162,52 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth,
|
||||
}
|
||||
}
|
||||
|
||||
void combineInfillLayers(int layerNr, SliceMeshStorage& storage, int amount)
|
||||
void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount)
|
||||
{
|
||||
SliceLayer* layer = &storage.layers[layerNr];
|
||||
|
||||
for(int n=1; n<amount; n++)
|
||||
if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
|
||||
{
|
||||
if (layerNr < n)
|
||||
break;
|
||||
|
||||
SliceLayer* layer2 = &storage.layers[layerNr - n];
|
||||
for(SliceLayerPart& part : layer->parts)
|
||||
return;
|
||||
}
|
||||
if(storage.layers.empty() || storage.layers.size() - 1 < static_cast<size_t>(storage.getSettingAsCount("top_layers")) || storage.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* We need to round down the layer index we start at to the nearest
|
||||
divisible index. Otherwise we get some parts that have infill at divisible
|
||||
layers and some at non-divisible layers. Those layers would then miss each
|
||||
other. */
|
||||
size_t min_layer = storage.getSettingAsCount("bottom_layers") + amount - 1;
|
||||
min_layer -= min_layer % amount; //Round upwards to the nearest layer divisible by infill_sparse_combine.
|
||||
size_t max_layer = storage.layers.size() - 1 - storage.getSettingAsCount("top_layers");
|
||||
max_layer -= max_layer % amount; //Round downwards to the nearest layer divisible by infill_sparse_combine.
|
||||
for(size_t layer_idx = min_layer;layer_idx <= max_layer;layer_idx += amount) //Skip every few layers, but extrude more.
|
||||
{
|
||||
SliceLayer* layer = &storage.layers[layer_idx];
|
||||
|
||||
for(unsigned int n = 1;n < amount;n++)
|
||||
{
|
||||
Polygons result;
|
||||
for(SliceLayerPart& part2 : layer2->parts)
|
||||
if(layer_idx < n)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
{
|
||||
Polygons intersection = part.infill_area[n - 1].intersection(part2.infill_area[0]).offset(-200).offset(200);
|
||||
result.add(intersection);
|
||||
part.infill_area[n - 1] = part.infill_area[n - 1].difference(intersection);
|
||||
part2.infill_area[0] = part2.infill_area[0].difference(intersection);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
SliceLayer* layer2 = &storage.layers[layer_idx - n];
|
||||
for(SliceLayerPart& part : layer->parts)
|
||||
{
|
||||
Polygons result;
|
||||
for(SliceLayerPart& part2 : layer2->parts)
|
||||
{
|
||||
if(part.boundaryBox.hit(part2.boundaryBox))
|
||||
{
|
||||
Polygons intersection = part.infill_area[n - 1].intersection(part2.infill_area[0]).offset(-200).offset(200);
|
||||
result.add(intersection);
|
||||
part.infill_area[n - 1] = part.infill_area[n - 1].difference(intersection);
|
||||
part2.infill_area[0] = part2.infill_area[0].difference(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
part.infill_area.push_back(result);
|
||||
}
|
||||
|
||||
part.infill_area.push_back(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-5
@@ -26,11 +26,14 @@ void generatePerimeterGaps(int layerNr, SliceMeshStorage& storage, int extrusion
|
||||
* \param extrusionWidth extrusionWidth
|
||||
* \param downSkinCount The number of layers of bottom skin
|
||||
* \param upSkinCount The number of layers of top skin
|
||||
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
|
||||
* \param innermost_wall_extrusion_width The line width of the inner most wall
|
||||
* \param insetCount The number of perimeters to surround the skin
|
||||
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
|
||||
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
|
||||
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p storage)
|
||||
*/
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
|
||||
/*!
|
||||
* Generate the skin areas (outlines)
|
||||
@@ -40,8 +43,10 @@ void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, i
|
||||
* \param extrusionWidth extrusionWidth
|
||||
* \param downSkinCount The number of layers of bottom skin
|
||||
* \param upSkinCount The number of layers of top skin
|
||||
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
|
||||
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
|
||||
*/
|
||||
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
|
||||
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic);
|
||||
|
||||
/*!
|
||||
* Generate the skin insets.
|
||||
@@ -56,14 +61,29 @@ void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidt
|
||||
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
|
||||
/*!
|
||||
* Generate Infill
|
||||
* Generate Infill by offsetting from the last wall.
|
||||
*
|
||||
* The walls should already be generated.
|
||||
*
|
||||
* \param layerNr The index of the layer for which to generate the infill
|
||||
* \param part The part where the insets (input) are stored and where the infill (output) is stored.
|
||||
* \param extrusionWidth width of the wall lines
|
||||
* \param infill_skin_overlap overlap distance between infill and skin
|
||||
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
|
||||
*/
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap);
|
||||
void combineInfillLayers(int layerNr, SliceMeshStorage& storage, int amount);
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap, int wall_line_count);
|
||||
|
||||
/*!
|
||||
* \brief Combines the infill of multiple layers for a specified mesh.
|
||||
*
|
||||
* The infill layers are combined while the thickness of each layer is
|
||||
* multiplied such that the infill should fill up again to the full height of
|
||||
* all combined layers.
|
||||
*
|
||||
* \param storage The mesh to combine the infill layers of.
|
||||
* \param amount The number of layers to combine.
|
||||
*/
|
||||
void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
+214
-18
@@ -1,5 +1,7 @@
|
||||
#include "sliceDataStorage.h"
|
||||
|
||||
#include "FffProcessor.h" //To create a mesh group with if none is provided.
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
@@ -25,33 +27,227 @@ void SliceLayer::getOutlines(Polygons& result, bool external_polys_only)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Polygons SliceDataStorage::getLayerOutlines(unsigned int layer_nr, bool include_helper_parts, bool external_polys_only)
|
||||
Polygons SliceLayer::getSecondOrInnermostWalls()
|
||||
{
|
||||
Polygons total;
|
||||
for (SliceMeshStorage& mesh : meshes)
|
||||
Polygons ret;
|
||||
getSecondOrInnermostWalls(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls)
|
||||
{
|
||||
for (SliceLayerPart& part : parts)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
layer.getOutlines(total, external_polys_only);
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
|
||||
// we want the 2nd inner walls
|
||||
if (part.insets.size() >= 2) {
|
||||
layer_walls.add(part.insets[1]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (include_helper_parts)
|
||||
{
|
||||
if (support.generated)
|
||||
{
|
||||
total.add(support.supportLayers[layer_nr].supportAreas);
|
||||
total.add(support.supportLayers[layer_nr].roofs);
|
||||
// but we'll also take the inner wall if the 2nd doesn't exist
|
||||
if (part.insets.size() == 1) {
|
||||
layer_walls.add(part.insets[0]);
|
||||
continue;
|
||||
}
|
||||
total.add(primeTower.ground_poly);
|
||||
// offset_from_outlines was so large that it completely destroyed our isle,
|
||||
// so we'll just use the regular outline
|
||||
layer_walls.add(part.outline);
|
||||
continue;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(meshgroup),
|
||||
meshgroup(meshgroup != nullptr ? meshgroup : new MeshGroup(FffProcessor::getInstance())), //If no mesh group is provided, we roll our own.
|
||||
retraction_config_per_extruder(initializeRetractionConfigs()),
|
||||
travel_config(&retraction_config, "MOVE"),
|
||||
skirt_config(initializeSkirtConfigs()),
|
||||
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
|
||||
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
|
||||
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
|
||||
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_infill_extruder_nr")], "SUPPORT"),
|
||||
support_roof_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_roof_extruder_nr")], "SKIN"),
|
||||
max_object_height_second_to_last_extruder(-1)
|
||||
{
|
||||
}
|
||||
|
||||
Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only)
|
||||
{
|
||||
if (layer_nr < 0)
|
||||
{ // when processing raft
|
||||
if (include_helper_parts)
|
||||
{
|
||||
if (external_polys_only)
|
||||
{
|
||||
std::vector<PolygonsPart> parts = raftOutline.splitIntoParts();
|
||||
Polygons result;
|
||||
for (PolygonsPart& part : parts)
|
||||
{
|
||||
result.add(part.outerPolygon());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return raftOutline;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Polygons();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Polygons total;
|
||||
for (SliceMeshStorage& mesh : meshes)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
layer.getOutlines(total, external_polys_only);
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
|
||||
}
|
||||
}
|
||||
if (include_helper_parts)
|
||||
{
|
||||
if (support.generated)
|
||||
{
|
||||
total.add(support.supportLayers[layer_nr].supportAreas);
|
||||
total.add(support.supportLayers[layer_nr].roofs);
|
||||
}
|
||||
total.add(primeTower.ground_poly);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts)
|
||||
{
|
||||
if (layer_nr < 0)
|
||||
{ // when processing raft
|
||||
if (include_helper_parts)
|
||||
{
|
||||
return raftOutline;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Polygons();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Polygons total;
|
||||
for (SliceMeshStorage& mesh : meshes)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
layer.getSecondOrInnermostWalls(total);
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
|
||||
}
|
||||
}
|
||||
if (include_helper_parts)
|
||||
{
|
||||
if (support.generated)
|
||||
{
|
||||
total.add(support.supportLayers[layer_nr].supportAreas);
|
||||
total.add(support.supportLayers[layer_nr].roofs);
|
||||
}
|
||||
total.add(primeTower.ground_poly);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::vector<bool> SliceDataStorage::getExtrudersUsed(int layer_nr)
|
||||
{
|
||||
std::vector<bool> ret;
|
||||
ret.resize(meshgroup->getExtruderCount(), false);
|
||||
if (layer_nr < 0)
|
||||
{
|
||||
ret[getSettingAsIndex("adhesion_extruder_nr")] = true; // raft
|
||||
}
|
||||
else
|
||||
{
|
||||
if (layer_nr == 0)
|
||||
{ // process brim/skirt
|
||||
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
|
||||
{
|
||||
if (skirt[extr_nr].size() > 0)
|
||||
{
|
||||
ret[extr_nr] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: ooze shield, draft shield
|
||||
|
||||
// support
|
||||
if (support.supportLayers[layer_nr].supportAreas.size() > 0)
|
||||
{
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret[getSettingAsIndex("support_extruder_nr")] = true;
|
||||
}
|
||||
}
|
||||
if (support.supportLayers[layer_nr].roofs.size() > 0)
|
||||
{
|
||||
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
|
||||
}
|
||||
|
||||
for (SliceMeshStorage& mesh : meshes)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
int extr_nr = mesh.getSettingAsIndex("extruder_nr");
|
||||
if (layer.parts.size() > 0)
|
||||
{
|
||||
ret[extr_nr] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector< bool > SliceDataStorage::getExtrudersUsed()
|
||||
{
|
||||
|
||||
std::vector<bool> ret;
|
||||
ret.resize(meshgroup->getExtruderCount(), false);
|
||||
|
||||
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
|
||||
{ // process brim/skirt
|
||||
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
|
||||
{
|
||||
if (skirt[extr_nr].size() > 0)
|
||||
{
|
||||
ret[extr_nr] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: ooze shield, draft shield ..?
|
||||
|
||||
// support
|
||||
// support is presupposed to be present...
|
||||
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
|
||||
ret[getSettingAsIndex("support_extruder_nr")] = true;
|
||||
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
|
||||
|
||||
// all meshes are presupposed to actually have content
|
||||
for (SliceMeshStorage& mesh : meshes)
|
||||
{
|
||||
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+89
-15
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "utils/NoCopy.h"
|
||||
#include "mesh.h"
|
||||
#include "gcodePlanner.h"
|
||||
#include "MeshGroup.h"
|
||||
@@ -23,6 +24,19 @@ public:
|
||||
std::vector<Polygons> insets; //!< The skin can have perimeters so that the skin lines always start at a perimeter instead of in the middle of an infill cell.
|
||||
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
|
||||
};
|
||||
|
||||
/*!
|
||||
* A ReinforcementWall is like an insulated wall behind the outer walls.
|
||||
* It consists of an area with (generally more dense) infill and perimeters on the inside.
|
||||
* On the outside it has the outer walls, or the inner walls of another ReinforcementWall.
|
||||
*/
|
||||
class ReinforcementWall
|
||||
{
|
||||
public:
|
||||
Polygons wall_reinforcement_area; //!< The infill of the reinforced wall
|
||||
std::vector<Polygons> wall_reinforcement_axtra_walls; //!< The extra walls on the inside of the reinforcement infill
|
||||
};
|
||||
|
||||
/*!
|
||||
The SliceLayerPart is a single enclosed printable area for a single layer. (Also known as islands)
|
||||
It's filled during the FffProcessor.processSliceData(.), where each step uses data from the previous steps.
|
||||
@@ -36,6 +50,7 @@ public:
|
||||
std::vector<Polygons> insets; //!< The insets are generated with: an offset of (index * line_width + line_width/2) compared to the outline. The insets are also known as perimeters, and printed inside out.
|
||||
std::vector<SkinPart> skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets.
|
||||
std::vector<Polygons> infill_area; //!< The infill_area are the areas which need to be filled with sparse (0-99%) infill. The infill_area is an array to support thicker layers of sparse infill. infill_area[n] is infill_area of (n+1) layers thick.
|
||||
std::vector<ReinforcementWall> reinforcement_walls; //!< The reinforcement walls for this part. Order: from outter to inner reinforcement wall.
|
||||
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
|
||||
};
|
||||
|
||||
@@ -51,8 +66,35 @@ public:
|
||||
std::vector<SliceLayerPart> parts; //!< An array of LayerParts which contain the actual data. The parts are printed one at a time to minimize travel outside of the 3D model.
|
||||
Polygons openPolyLines; //!< A list of lines which were never hooked up into a 2D polygon. (Currently unused in normal operation)
|
||||
|
||||
/*!
|
||||
* Get the all outlines of all layer parts in this layer.
|
||||
*
|
||||
* \param external_polys_only Whether to only include the outermost outline of each layer part
|
||||
* \return A collection of all the outline polygons
|
||||
*/
|
||||
Polygons getOutlines(bool external_polys_only = false);
|
||||
|
||||
/*!
|
||||
* Get the all outlines of all layer parts in this layer.
|
||||
* Add those polygons to @p result.
|
||||
*
|
||||
* \param external_polys_only Whether to only include the outermost outline of each layer part
|
||||
* \param result The result: a collection of all the outline polygons
|
||||
*/
|
||||
void getOutlines(Polygons& result, bool external_polys_only = false);
|
||||
|
||||
/*!
|
||||
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
|
||||
* \return The collection of all polygons thus obtained
|
||||
*/
|
||||
Polygons getSecondOrInnermostWalls();
|
||||
|
||||
/*!
|
||||
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
|
||||
* Add those polygons to @p result.
|
||||
* \param result The result: the collection of all polygons thus obtained
|
||||
*/
|
||||
void getSecondOrInnermostWalls(Polygons& result);
|
||||
};
|
||||
|
||||
/******************/
|
||||
@@ -89,9 +131,10 @@ public:
|
||||
GCodePathConfig insetX_config;
|
||||
GCodePathConfig skin_config;
|
||||
std::vector<GCodePathConfig> infill_config;
|
||||
GCodePathConfig wall_reinforcement_config;
|
||||
|
||||
SliceMeshStorage(SettingsBaseVirtual* settings)
|
||||
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, "WALL-OUTER"), insetX_config(&retraction_config, "WALL-INNER"), skin_config(&retraction_config, "SKIN")
|
||||
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, "WALL-OUTER"), insetX_config(&retraction_config, "WALL-INNER"), skin_config(&retraction_config, "SKIN"), wall_reinforcement_config(&retraction_config, "SUPPORT")
|
||||
{
|
||||
infill_config.reserve(MAX_INFILL_COMBINE);
|
||||
for(int n=0; n<MAX_INFILL_COMBINE; n++)
|
||||
@@ -99,7 +142,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class SliceDataStorage : public SettingsMessenger
|
||||
class SliceDataStorage : public SettingsMessenger, NoCopy
|
||||
{
|
||||
public:
|
||||
MeshGroup* meshgroup; // needed to pass on the per extruder settings.. (TODO: put this somewhere else? Put the per object settings here directly, or a pointer only to the per object settings.)
|
||||
@@ -110,9 +153,14 @@ public:
|
||||
std::vector<RetractionConfig> retraction_config_per_extruder; //!< used for support, skirt, etc.
|
||||
RetractionConfig retraction_config; //!< The retraction config used as fallback when getting the per_extruder_config or the mesh config was impossible (for travelConfig)
|
||||
|
||||
GCodePathConfig travel_config; //!< The config used for travel moves (only the speed and retraction config are set!)
|
||||
std::vector<GCodePathConfig> skirt_config; //!< config for skirt per extruder
|
||||
std::vector<CoastingConfig> coasting_config; //!< coasting config per extruder
|
||||
|
||||
GCodePathConfig raft_base_config;
|
||||
GCodePathConfig raft_interface_config;
|
||||
GCodePathConfig raft_surface_config;
|
||||
|
||||
GCodePathConfig support_config;
|
||||
GCodePathConfig support_roof_config;
|
||||
|
||||
@@ -144,17 +192,18 @@ public:
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
SliceDataStorage(MeshGroup* meshgroup)
|
||||
: SettingsMessenger(meshgroup)
|
||||
, meshgroup(meshgroup)
|
||||
, retraction_config_per_extruder(initializeRetractionConfigs())
|
||||
, skirt_config(initializeSkirtConfigs())
|
||||
, support_config(&retraction_config_per_extruder[meshgroup->getSettingAsIndex("support_extruder_nr")], "SUPPORT")
|
||||
, support_roof_config(&retraction_config_per_extruder[meshgroup->getSettingAsIndex("support_roof_extruder_nr")], "SKIN")
|
||||
, max_object_height_second_to_last_extruder(-1)
|
||||
// , primeTower()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Creates a new slice data storage that stores the slice data of the
|
||||
* specified mesh group.
|
||||
*
|
||||
* It will obtain the settings from the mesh group too. The mesh group is
|
||||
* not yet sliced in this constructor. If no mesh group is provided, an
|
||||
* empty one will be created.
|
||||
*
|
||||
* \param meshgroup The mesh group to load into this data storage, if any.
|
||||
*/
|
||||
SliceDataStorage(MeshGroup* meshgroup);
|
||||
|
||||
~SliceDataStorage()
|
||||
{
|
||||
@@ -163,11 +212,36 @@ public:
|
||||
/*!
|
||||
* Get all outlines within a given layer.
|
||||
*
|
||||
* \param layer_nr the index of the layer for which to get the outlines
|
||||
* \param layer_nr the index of the layer for which to get the outlines (negative layer numbers indicate the raft)
|
||||
* \param include_helper_parts whether to include support and prime tower
|
||||
* \param external_polys_only whether to disregard all hole polygons
|
||||
*/
|
||||
Polygons getLayerOutlines(unsigned int layer_nr, bool include_helper_parts, bool external_polys_only = false);
|
||||
Polygons getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only = false);
|
||||
|
||||
/*!
|
||||
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
|
||||
*
|
||||
* For helper parts the outlines are used.
|
||||
*
|
||||
* \param layer_nr the index of the layer for which to get the outlines (negative layer numbers indicate the raft)
|
||||
* \param include_helper_parts whether to include support and prime tower
|
||||
*/
|
||||
Polygons getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts);
|
||||
|
||||
/*!
|
||||
* Get the extruder numbers of all extruders used in a given layer.
|
||||
*
|
||||
* \param layer_nr the index of the layer for which to get the extruders used (negative layer numbers indicate the raft)
|
||||
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
|
||||
*/
|
||||
std::vector<bool> getExtrudersUsed(int layer_nr);
|
||||
|
||||
/*!
|
||||
* Get the extruders used.
|
||||
*
|
||||
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
|
||||
*/
|
||||
std::vector<bool> getExtrudersUsed();
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+28
-25
@@ -41,11 +41,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
|
||||
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
|
||||
storage.support.supportLayers.emplace_back();
|
||||
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
for(unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
|
||||
{
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
std::vector<Polygons> supportAreas;
|
||||
supportAreas.resize(layer_count, Polygons());
|
||||
generateSupportAreas(storage, &mesh, layer_count, supportAreas, commandSocket);
|
||||
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas, commandSocket);
|
||||
|
||||
if (mesh.getSettingBoolean("support_roof_enable"))
|
||||
{
|
||||
@@ -79,43 +80,45 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
|
||||
*
|
||||
* for support buildplate only: purge all support not connected to buildplate
|
||||
*/
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket)
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket)
|
||||
{
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
|
||||
// given settings
|
||||
ESupportType support_type = object->getSettingAsSupportType("support_type");
|
||||
ESupportType support_type = mesh.getSettingAsSupportType("support_type");
|
||||
|
||||
if (!object->getSettingBoolean("support_enable"))
|
||||
if (!mesh.getSettingBoolean("support_enable"))
|
||||
return;
|
||||
if (support_type == ESupportType::NONE)
|
||||
return;
|
||||
|
||||
double supportAngle = object->getSettingInAngleRadians("support_angle");
|
||||
double supportAngle = mesh.getSettingInAngleRadians("support_angle");
|
||||
bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
|
||||
int supportZDistance = object->getSettingInMicrons("support_z_distance");
|
||||
int supportZDistanceBottom = object->getSettingInMicrons("support_bottom_distance");
|
||||
int supportZDistanceTop = object->getSettingInMicrons("support_top_distance");
|
||||
int join_distance = object->getSettingInMicrons("support_join_distance");
|
||||
int support_bottom_stair_step_height = object->getSettingInMicrons("support_bottom_stair_step_height");
|
||||
int smoothing_distance = object->getSettingInMicrons("support_area_smoothing");
|
||||
int supportZDistance = mesh.getSettingInMicrons("support_z_distance");
|
||||
int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance");
|
||||
int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance");
|
||||
int join_distance = mesh.getSettingInMicrons("support_join_distance");
|
||||
int support_bottom_stair_step_height = mesh.getSettingInMicrons("support_bottom_stair_step_height");
|
||||
int smoothing_distance = mesh.getSettingInMicrons("support_area_smoothing");
|
||||
|
||||
int extension_offset = object->getSettingInMicrons("support_offset");
|
||||
int extension_offset = mesh.getSettingInMicrons("support_offset");
|
||||
|
||||
int supportTowerDiameter = object->getSettingInMicrons("support_tower_diameter");
|
||||
int supportMinAreaSqrt = object->getSettingInMicrons("support_minimal_diameter");
|
||||
double supportTowerRoofAngle = object->getSettingInAngleRadians("support_tower_roof_angle");
|
||||
int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter");
|
||||
int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter");
|
||||
double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle");
|
||||
|
||||
//std::cerr <<" towerDiameter=" << towerDiameter <<", supportMinAreaSqrt=" << supportMinAreaSqrt << std::endl;
|
||||
|
||||
int min_smoothing_area = 100*100; // minimal area for which to perform smoothing
|
||||
int z_layer_distance_tower = 1; // start tower directly below overhang point
|
||||
|
||||
int layerThickness = object->getSettingInMicrons("layer_height");
|
||||
int extrusionWidth = object->getSettingInMicrons("support_line_width");
|
||||
int supportXYDistance = object->getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
|
||||
int layerThickness = mesh.getSettingInMicrons("layer_height");
|
||||
int extrusionWidth = mesh.getSettingInMicrons("support_line_width");
|
||||
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
|
||||
|
||||
bool conical_support = object->getSettingBoolean("support_conical_enabled");
|
||||
double conical_support_angle = object->getSettingInAngleRadians("support_conical_angle");
|
||||
int64_t conical_smallest_breadth = object->getSettingInMicrons("support_conical_min_width");
|
||||
bool conical_support = mesh.getSettingBoolean("support_conical_enabled");
|
||||
double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
|
||||
int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width");
|
||||
|
||||
// derived settings:
|
||||
|
||||
@@ -160,7 +163,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
|
||||
|
||||
|
||||
std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs
|
||||
AreaSupport::detectOverhangPoints(storage, *object, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
|
||||
AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
|
||||
|
||||
|
||||
bool still_in_upper_empty_layers = true;
|
||||
@@ -173,7 +176,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
|
||||
Polygons overhang;
|
||||
{
|
||||
// compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
|
||||
Polygons supportLayer_supportee = object->layers[layer_idx+layerZdistanceTop].getOutlines();
|
||||
Polygons supportLayer_supportee = mesh.layers[layer_idx+layerZdistanceTop].getOutlines();
|
||||
Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1+layerZdistanceTop, false);
|
||||
|
||||
Polygons supportLayer_supported = supportLayer_supporter.offset(maxDistFromLowerLayer);
|
||||
@@ -249,7 +252,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
|
||||
still_in_upper_empty_layers = false;
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::SUPPORT, support_layer_count - layer_idx, support_layer_count, commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size(), commandSocket);
|
||||
}
|
||||
|
||||
// do stuff for when support on buildplate only
|
||||
|
||||
+2
-2
@@ -26,11 +26,11 @@ private:
|
||||
* This function also handles small overhang areas (creates towers with larger diameter than just the overhang area) and single walls which could otherwise fall over.
|
||||
*
|
||||
* \param storage data storage containing the input layer outline data
|
||||
* \param object The object for which to generate support areas
|
||||
* \param mesh_idx The index of the object for which to generate support areas
|
||||
* \param layer_count total number of layers
|
||||
* \param commandSocket Socket over which to report the progress
|
||||
*/
|
||||
static void generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket);
|
||||
static void generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -24,8 +24,15 @@ void TimeEstimateCalculator::setPosition(Position newPos)
|
||||
currentPosition = newPos;
|
||||
}
|
||||
|
||||
void TimeEstimateCalculator::addTime(double time)
|
||||
{
|
||||
extra_time += time;
|
||||
}
|
||||
|
||||
|
||||
void TimeEstimateCalculator::reset()
|
||||
{
|
||||
extra_time = 0.0;
|
||||
blocks.clear();
|
||||
}
|
||||
|
||||
@@ -190,7 +197,7 @@ double TimeEstimateCalculator::calculate()
|
||||
forward_pass();
|
||||
recalculate_trapezoids();
|
||||
|
||||
double totalTime = 0;
|
||||
double totalTime = extra_time;
|
||||
for(unsigned int n=0; n<blocks.size(); n++)
|
||||
{
|
||||
Block& block = blocks[n];
|
||||
@@ -287,7 +294,7 @@ void TimeEstimateCalculator::recalculate_trapezoids()
|
||||
Block *current;
|
||||
Block *next = nullptr;
|
||||
|
||||
for(unsigned int n=0; n<blocks.size(); n--)
|
||||
for(unsigned int n=0; n<blocks.size(); n++)
|
||||
{
|
||||
current = next;
|
||||
next = &blocks[n];
|
||||
|
||||
+12
-4
@@ -7,10 +7,10 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/**
|
||||
The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
|
||||
Some of this code has been addapted from the Marlin sources.
|
||||
*/
|
||||
/*!
|
||||
* The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
|
||||
* Some of this code has been adapted from the Marlin sources.
|
||||
*/
|
||||
|
||||
class TimeEstimateCalculator
|
||||
{
|
||||
@@ -54,6 +54,8 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
double extra_time;
|
||||
|
||||
Position previous_feedrate;
|
||||
double previous_nominal_feedrate;
|
||||
|
||||
@@ -61,8 +63,14 @@ private:
|
||||
|
||||
std::vector<Block> blocks;
|
||||
public:
|
||||
TimeEstimateCalculator()
|
||||
: extra_time(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
void setPosition(Position newPos);
|
||||
void plan(Position newPos, double feedRate);
|
||||
void addTime(double time);
|
||||
void reset();
|
||||
|
||||
double calculate();
|
||||
|
||||
+18
-6
@@ -18,7 +18,7 @@ public:
|
||||
Point min, max;
|
||||
|
||||
AABB()
|
||||
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
|
||||
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
|
||||
{
|
||||
}
|
||||
AABB(Point&min, Point& max)
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
{
|
||||
}
|
||||
AABB(Polygons& polys)
|
||||
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
|
||||
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
|
||||
{
|
||||
calculate(polys);
|
||||
}
|
||||
@@ -39,10 +39,7 @@ public:
|
||||
{
|
||||
for(unsigned int j=0; j<polys[i].size(); j++)
|
||||
{
|
||||
if (min.X > polys[i][j].X) min.X = polys[i][j].X;
|
||||
if (min.Y > polys[i][j].Y) min.Y = polys[i][j].Y;
|
||||
if (max.X < polys[i][j].X) max.X = polys[i][j].X;
|
||||
if (max.Y < polys[i][j].Y) max.Y = polys[i][j].Y;
|
||||
include(polys[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,6 +52,21 @@ public:
|
||||
if (min.Y > other.max.Y) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Includes the specified point in the bounding box.
|
||||
*
|
||||
* The bounding box is expanded if the point is not within the bounding box.
|
||||
*
|
||||
* \param point The point to include in the bounding box.
|
||||
*/
|
||||
void include(Point point)
|
||||
{
|
||||
min.X = std::min(min.X,point.X);
|
||||
min.Y = std::min(min.Y,point.Y);
|
||||
max.X = std::max(max.X,point.X);
|
||||
max.Y = std::max(max.Y,point.Y);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
+11
-7
@@ -1,9 +1,9 @@
|
||||
#ifndef UTILS_NO_COPY_H
|
||||
#define UTILS_NO_COPY_H
|
||||
|
||||
/**
|
||||
Util class to base other objects off which should never be copied.
|
||||
Is needed because C++ has an implicit copy constructor and assign operator when none are defined.
|
||||
/*!
|
||||
* Util class to base other objects off which should never be copied.
|
||||
* Is needed because C++ has an implicit copy constructor and assign operator when none are defined.
|
||||
*/
|
||||
class NoCopy
|
||||
{
|
||||
@@ -11,12 +11,16 @@ protected:
|
||||
NoCopy() {}
|
||||
|
||||
private:
|
||||
/// Private copy constructor.
|
||||
/// Cannot be called because it is private.
|
||||
/*!
|
||||
* Private copy constructor.
|
||||
* Cannot be called because it is private.
|
||||
*/
|
||||
NoCopy(const NoCopy&);
|
||||
|
||||
/// Private assign operator.
|
||||
/// Cannot be called because it is private.
|
||||
/*!
|
||||
* Private assign operator.
|
||||
* Cannot be called because it is private.
|
||||
*/
|
||||
NoCopy& operator =(const NoCopy&);
|
||||
};
|
||||
|
||||
|
||||
+74
-11
@@ -6,6 +6,7 @@
|
||||
#include "polygon.h"
|
||||
#include "intpoint.h"
|
||||
#include "AABB.h"
|
||||
#include "logoutput.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
@@ -18,7 +19,8 @@ public:
|
||||
GRAY,
|
||||
RED,
|
||||
BLUE,
|
||||
GREEN
|
||||
GREEN,
|
||||
YELLOW
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -33,6 +35,7 @@ private:
|
||||
case SVG::Color::RED: return "red";
|
||||
case SVG::Color::BLUE: return "blue";
|
||||
case SVG::Color::GREEN: return "green";
|
||||
case SVG::Color::YELLOW: return "yellow";
|
||||
default: return "black";
|
||||
}
|
||||
}
|
||||
@@ -51,8 +54,12 @@ public:
|
||||
, scale(std::min(double(canvas_size.X - 20) / aabb_size.X, double(canvas_size.Y - 20) / aabb_size.Y))
|
||||
{
|
||||
out = fopen(filename, "w");
|
||||
if(!out)
|
||||
{
|
||||
logError("The file %s could not be opened for writing.",filename);
|
||||
}
|
||||
fprintf(out, "<!DOCTYPE html><html><body>\n");
|
||||
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width:%llipx;height:%llipx\">\n", canvas_size.Y, canvas_size.X);
|
||||
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width:%llipx;height:%llipx\">\n", canvas_size.X, canvas_size.Y);
|
||||
|
||||
// fprintf(out, "<marker id='MidMarker' viewBox='0 0 10 10' refX='5' refY='5' markerUnits='strokeWidth' markerWidth='10' markerHeight='10' stroke='lightblue' stroke-width='2' fill='none' orient='auto'>");
|
||||
// fprintf(out, "<path d='M 0 0 L 10 5 M 0 10 L 10 5'/>");
|
||||
@@ -69,7 +76,7 @@ public:
|
||||
/*!
|
||||
* transform a point in real space to canvas space
|
||||
*/
|
||||
Point transform(Point& p)
|
||||
Point transform(const Point& p)
|
||||
{
|
||||
return Point((p.X-aabb.min.X)*scale, (p.Y-aabb.min.Y)*scale) + Point(10,10);
|
||||
}
|
||||
@@ -123,7 +130,7 @@ public:
|
||||
for(Point& p : poly)
|
||||
{
|
||||
Point fp = transform(p);
|
||||
fprintf(out, "%lli,%lli ", fp.Y, fp.X);
|
||||
fprintf(out, "%lli,%lli ", fp.X, fp.Y);
|
||||
}
|
||||
if (j == 0)
|
||||
fprintf(out, "\" style=\"fill:%s;stroke:%s;stroke-width:1\" />\n", toString(color).c_str(), toString(outline_color).c_str());
|
||||
@@ -133,14 +140,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void writePoint(Point& p, bool write_coords=false, int size = 5, Color color = Color::BLACK)
|
||||
void writeAreas(std::vector<Point> polygon,Color color = Color::GRAY,Color outline_color = Color::BLACK)
|
||||
{
|
||||
fprintf(out,"<polygon fill=\"%s\" stroke=\"%s\" stroke-width=\"1\" points=\"",toString(color).c_str(),toString(outline_color).c_str()); //The beginning of the polygon tag.
|
||||
for(Point& point : polygon) //Add every point to the list of points.
|
||||
{
|
||||
Point transformed = transform(point);
|
||||
fprintf(out,"%lli,%lli ",transformed.X,transformed.Y);
|
||||
}
|
||||
fprintf(out,"\" />\n"); //The end of the polygon tag.
|
||||
}
|
||||
|
||||
void writePoint(const Point& p, bool write_coords=false, int size = 5, Color color = Color::BLACK)
|
||||
{
|
||||
Point pf = transform(p);
|
||||
fprintf(out, "<circle cx=\"%lli\" cy=\"%lli\" r=\"%d\" stroke=\"%s\" stroke-width=\"1\" fill=\"%s\" />\n",pf.Y, pf.X, size, toString(color).c_str(), toString(color).c_str());
|
||||
fprintf(out, "<circle cx=\"%lli\" cy=\"%lli\" r=\"%d\" stroke=\"%s\" stroke-width=\"1\" fill=\"%s\" />\n",pf.X, pf.Y, size, toString(color).c_str(), toString(color).c_str());
|
||||
|
||||
if (write_coords)
|
||||
{
|
||||
fprintf(out, "<text x=\"%lli\" y=\"%lli\" style=\"font-size: 10;\" fill=\"black\">%lli,%lli</text>\n",pf.Y, pf.X, p.X, p.Y);
|
||||
fprintf(out, "<text x=\"%lli\" y=\"%lli\" style=\"font-size: 10;\" fill=\"black\">%lli,%lli</text>\n",pf.X, pf.Y, p.X, p.Y);
|
||||
}
|
||||
}
|
||||
void writePoints(PolygonRef poly, bool write_coords=false, int size = 5, Color color = Color::BLACK)
|
||||
@@ -159,14 +177,59 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void writeLine(Point& a, Point& b, Color color = Color::BLACK)
|
||||
/*!
|
||||
* \brief Draws a polyline on the canvas.
|
||||
*
|
||||
* The polyline is the set of line segments between each pair of consecutive
|
||||
* points in the specified vector.
|
||||
*
|
||||
* \param polyline A set of points between which line segments must be
|
||||
* drawn.
|
||||
* \param color The colour of the line segments. If this is not specified,
|
||||
* black will be used.
|
||||
*/
|
||||
void writeLines(std::vector<Point> polyline,Color color = Color::BLACK)
|
||||
{
|
||||
if(polyline.size() <= 1) //Need at least 2 points.
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:1\" />", a.Y, a.X, b.Y, b.X, toString(color).c_str());
|
||||
Point transformed = transform(polyline[0]); //Element 0 must exist due to the check above.
|
||||
fprintf(out,"<path fill=\"none\" stroke=\"%s\" stroke-width=\"1\" d=\"M%lli,%lli",toString(color).c_str(),transformed.X,transformed.Y); //Write the start of the path tag and the first endpoint.
|
||||
for(size_t point = 1;point < polyline.size();point++)
|
||||
{
|
||||
transformed = transform(polyline[point]);
|
||||
fprintf(out,"L%lli,%lli",transformed.X,transformed.Y); //Write a line segment to the next point.
|
||||
}
|
||||
fprintf(out,"\" />\n"); //Write the end of the tag.
|
||||
}
|
||||
|
||||
void writeLine(const Point& a, const Point& b, Color color = Color::BLACK)
|
||||
{
|
||||
Point fa = transform(a);
|
||||
Point fb = transform(b);
|
||||
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:1\" />\n", fa.X, fa.Y, fb.X, fb.Y, toString(color).c_str());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Draws a dashed line on the canvas from point A to point B.
|
||||
*
|
||||
* This is useful in the case where multiple lines may overlap each other.
|
||||
*
|
||||
* \param a The starting endpoint of the line.
|
||||
* \param b The ending endpoint of the line.
|
||||
* \param color The stroke colour of the line.
|
||||
*/
|
||||
void writeDashedLine(const Point& a,const Point& b,Color color = Color::BLACK)
|
||||
{
|
||||
Point fa = transform(a);
|
||||
Point fb = transform(b);
|
||||
fprintf(out,"<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" stroke=\"%s\" stroke-width=\"1\" stroke-dasharray=\"5,5\" />\n",fa.X,fa.Y,fb.X,fb.Y,toString(color).c_str());
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void printf(const char* txt, Args... args)
|
||||
void printf(const char* txt, Args&&... args)
|
||||
{
|
||||
fprintf(out, txt, args...);
|
||||
}
|
||||
@@ -213,4 +276,4 @@ public:
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
#endif // SVG_H
|
||||
#endif // SVG_H
|
||||
|
||||
@@ -126,6 +126,8 @@ public:
|
||||
|
||||
};
|
||||
|
||||
static Point3 no_point3(std::numeric_limits<int32_t>::infinity(), std::numeric_limits<int32_t>::infinity(), std::numeric_limits<int32_t>::infinity());
|
||||
|
||||
inline Point3 operator*(const int32_t i, const Point3& rhs) {
|
||||
return rhs * i;
|
||||
}
|
||||
@@ -145,6 +147,8 @@ public:
|
||||
#define POINT_MIN std::numeric_limits<ClipperLib::cInt>::min()
|
||||
#define POINT_MAX std::numeric_limits<ClipperLib::cInt>::max()
|
||||
|
||||
static Point no_point(std::numeric_limits<int32_t>::infinity(), std::numeric_limits<int32_t>::infinity());
|
||||
|
||||
/* Extra operators to make it easier to do math with the 64bit Point objects */
|
||||
INLINE Point operator-(const Point& p0) { return Point(-p0.X, -p0.Y); }
|
||||
INLINE Point operator+(const Point& p0, const Point& p1) { return Point(p0.X+p1.X, p0.Y+p1.Y); }
|
||||
@@ -307,3 +311,4 @@ inline Point operator-(const Point& p2, const Point3& p3) {
|
||||
|
||||
}//namespace cura
|
||||
#endif//INT_POINT_H
|
||||
|
||||
|
||||
@@ -103,12 +103,14 @@ public:
|
||||
/*!
|
||||
* Get the squared distance from point \p b to a line *segment* from \p a to \p c.
|
||||
*
|
||||
* In case \p b is on \p a or \p c, \p b_is_beyond_ac should become 0.
|
||||
*
|
||||
* \param a the first point of the line segment
|
||||
* \param b the point to measure the distance from
|
||||
* \param c the second point on the line segment
|
||||
* \param b_is_beyond_ac optional output parameter: whether \p b is closest to the line segment (0), to \p a (-1) or \p b (1)
|
||||
*/
|
||||
static int64_t getDist2FromLineSegment(Point& a, Point& b, Point& c, char* b_is_beyond_ac = nullptr)
|
||||
static int64_t getDist2FromLineSegment(const Point& a, const Point& b, const Point& c, char* b_is_beyond_ac = nullptr)
|
||||
{
|
||||
/*
|
||||
* a,
|
||||
@@ -127,27 +129,46 @@ public:
|
||||
*/
|
||||
Point ac = c - a;
|
||||
int64_t ac_size = vSize(ac);
|
||||
if (ac_size == 0) { return 0; }
|
||||
|
||||
|
||||
Point ab = b - a;
|
||||
if (ac_size == 0)
|
||||
{
|
||||
int64_t ab_dist2 = vSize2(ab);
|
||||
if (ab_dist2 == 0)
|
||||
{
|
||||
*b_is_beyond_ac = 0; // a is on b is on c
|
||||
}
|
||||
// otherwise variable b_is_beyond_ac remains its value; it doesn't make sense to choose between -1 and 1
|
||||
return ab_dist2;
|
||||
}
|
||||
int64_t projected_x = dot(ab, ac);
|
||||
int64_t ax_size = projected_x / ac_size;
|
||||
|
||||
if (ax_size < 0)
|
||||
{// b is 'before' segment ac
|
||||
if (b_is_beyond_ac) { *b_is_beyond_ac = -1; }
|
||||
if (b_is_beyond_ac)
|
||||
{
|
||||
*b_is_beyond_ac = -1;
|
||||
}
|
||||
return vSize2(ab);
|
||||
}
|
||||
if (ax_size > ac_size)
|
||||
{// b is 'after' segment ac
|
||||
if (b_is_beyond_ac) { *b_is_beyond_ac = 1; }
|
||||
if (b_is_beyond_ac)
|
||||
{
|
||||
*b_is_beyond_ac = 1;
|
||||
}
|
||||
return vSize2(b - c);
|
||||
}
|
||||
|
||||
if (b_is_beyond_ac)
|
||||
{
|
||||
*b_is_beyond_ac = 0;
|
||||
}
|
||||
Point ax = ac * ax_size / ac_size;
|
||||
Point bx = ab - ax;
|
||||
if (b_is_beyond_ac) { *b_is_beyond_ac = 0; }
|
||||
return vSize2(bx);
|
||||
// return vSize2(ab) - ax_size*ax_size; // less accurate
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ void PolygonRef::simplify(int smallest_line_segment_squared, int allowed_error_d
|
||||
continue;
|
||||
}
|
||||
Point& next = thiss[(poly_idx+1) % size()];
|
||||
char here_is_beyond_line;
|
||||
char here_is_beyond_line = 0;
|
||||
int64_t error2 = LinearAlg2D::getDist2FromLineSegment(*last, here, next, &here_is_beyond_line);
|
||||
if (here_is_beyond_line == 0 && error2 < allowed_error_distance_squared)
|
||||
{// don't add the point to the result
|
||||
|
||||
@@ -30,7 +30,9 @@ enum PolygonType
|
||||
SupportType,
|
||||
SkirtType,
|
||||
InfillType,
|
||||
SupportInfillType
|
||||
SupportInfillType,
|
||||
MoveCombingType,
|
||||
MoveRetractionType
|
||||
};
|
||||
|
||||
|
||||
@@ -81,7 +83,7 @@ public:
|
||||
ClipperLib::Path& operator*() { return *polygon; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args... args)
|
||||
void emplace_back(Args&&... args)
|
||||
{
|
||||
polygon->emplace_back(args...);
|
||||
}
|
||||
@@ -357,6 +359,7 @@ public:
|
||||
}
|
||||
|
||||
Polygons() {}
|
||||
|
||||
Polygons(const Polygons& other) { polygons = other.polygons; }
|
||||
Polygons& operator=(const Polygons& other) { polygons = other.polygons; return *this; }
|
||||
Polygons difference(const Polygons& other) const
|
||||
|
||||
@@ -104,8 +104,13 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
Point ab = b - a;
|
||||
Point ap = p - a;
|
||||
int64_t ab_length = vSize(ab);
|
||||
if(ab_length <= 0) //A = B, i.e. the input polygon had two adjacent points on top of each other.
|
||||
{
|
||||
p1 = p2; //Skip only one of the points.
|
||||
continue;
|
||||
}
|
||||
int64_t ax_length = dot(ab, ap) / ab_length;
|
||||
if (ax_length < 0) // x is projected to before ab
|
||||
if (ax_length <= 0) // x is projected to before ab
|
||||
{
|
||||
if (projected_p_beyond_prev_segment)
|
||||
{ // case which looks like: > .
|
||||
@@ -120,7 +125,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
if (distance == 0) { ret = x; }
|
||||
else
|
||||
{
|
||||
Point inward_dir = crossZ(normal(a, distance*4) + normal(p1 - p0, distance*4));
|
||||
Point inward_dir = crossZ(normal(ab,distance * 4) + normal(p1 - p0,distance * 4));
|
||||
ret = x + normal(inward_dir, distance); // *4 to retain more precision for the eventual normalization
|
||||
is_inside = dot(inward_dir, p - x) >= 0;
|
||||
}
|
||||
@@ -134,7 +139,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (ax_length > ab_length) // x is projected to beyond ab
|
||||
else if (ax_length >= ab_length) // x is projected to beyond ab
|
||||
{
|
||||
projected_p_beyond_prev_segment = true;
|
||||
p0 = p1;
|
||||
@@ -449,11 +454,19 @@ bool PolygonUtils::polygonCollidesWithlineSegment(PolygonRef poly, Point& transf
|
||||
for(Point p1_ : poly)
|
||||
{
|
||||
Point p1 = transformation_matrix.apply(p1_);
|
||||
if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y))
|
||||
if ((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y))
|
||||
{
|
||||
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
|
||||
int64_t x;
|
||||
if(p1.Y == p0.Y)
|
||||
{
|
||||
x = p0.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
|
||||
}
|
||||
|
||||
if (x > transformed_startPoint.X && x < transformed_endPoint.X)
|
||||
if (x >= transformed_startPoint.X && x <= transformed_endPoint.X)
|
||||
return true;
|
||||
}
|
||||
p0 = p1;
|
||||
@@ -461,7 +474,6 @@ bool PolygonUtils::polygonCollidesWithlineSegment(PolygonRef poly, Point& transf
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PolygonUtils::polygonCollidesWithlineSegment(PolygonRef poly, Point& startPoint, Point& endPoint)
|
||||
{
|
||||
Point diff = endPoint - startPoint;
|
||||
|
||||
+64
-32
@@ -136,47 +136,79 @@ public:
|
||||
|
||||
|
||||
/*!
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
* The transformed_startPoint and transformed_endPoint should have the same Y coordinate.
|
||||
*
|
||||
* \param poly The polygon
|
||||
* \param transformed_startPoint The start point transformed such that it is on the same horizontal line as the end point
|
||||
* \param transformed_endPoint The end point transformed such that it is on the same horizontal line as the start point
|
||||
* \param transformation_matrix The transformation applied to the start and end point to be applied to the polygon(s)
|
||||
* \return whether the line segment collides with the boundary of the polygon(s)
|
||||
*/
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
* The transformed_startPoint and transformed_endPoint should have the same
|
||||
* Y coordinate.
|
||||
*
|
||||
* If the line segment doesn't intersect with any edge of the polygon, but
|
||||
* merely touches it, a collision is also reported. For instance, a
|
||||
* collision is reported when the an endpoint of the line is exactly on the
|
||||
* polygon, and when the line coincides with an edge.
|
||||
*
|
||||
* \param poly The polygon
|
||||
* \param transformed_startPoint The start point transformed such that it is
|
||||
* on the same horizontal line as the end point
|
||||
* \param transformed_endPoint The end point transformed such that it is on
|
||||
* the same horizontal line as the start point
|
||||
* \param transformation_matrix The transformation applied to the start and
|
||||
* end point to be applied to the polygon(s)
|
||||
* \return whether the line segment collides with the boundary of the
|
||||
* polygon(s)
|
||||
*/
|
||||
static bool polygonCollidesWithlineSegment(PolygonRef poly, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix);
|
||||
|
||||
/*!
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
*
|
||||
* \param poly The polygon
|
||||
* \param startPoint The start point
|
||||
* \param endPoint The end point
|
||||
* \return whether the line segment collides with the boundary of the polygon(s)
|
||||
*/
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
*
|
||||
* If the line segment doesn't intersect with any edge of the polygon, but
|
||||
* merely touches it, a collision is also reported. For instance, a
|
||||
* collision is reported when the an endpoint of the line is exactly on the
|
||||
* polygon, and when the line coincides with an edge.
|
||||
*
|
||||
* \param poly The polygon
|
||||
* \param startPoint The start point
|
||||
* \param endPoint The end point
|
||||
* \return whether the line segment collides with the boundary of the
|
||||
* polygon(s)
|
||||
*/
|
||||
static bool polygonCollidesWithlineSegment(PolygonRef poly, Point& startPoint, Point& endPoint);
|
||||
|
||||
/*!
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
* The transformed_startPoint and transformed_endPoint should have the same Y coordinate.
|
||||
*
|
||||
* \param poly The polygon
|
||||
* \param transformed_startPoint The start point transformed such that it is on the same horizontal line as the end point
|
||||
* \param transformed_endPoint The end point transformed such that it is on the same horizontal line as the start point
|
||||
* \param transformation_matrix The transformation applied to the start and end point to be applied to the polygon(s)
|
||||
* \return whether the line segment collides with the boundary of the polygon(s)
|
||||
*/
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
* The transformed_startPoint and transformed_endPoint should have the same
|
||||
* Y coordinate.
|
||||
*
|
||||
* If the line segment doesn't intersect with any edge of the polygon, but
|
||||
* merely touches it, a collision is also reported. For instance, a
|
||||
* collision is reported when the an endpoint of the line is exactly on the
|
||||
* polygon, and when the line coincides with an edge.
|
||||
*
|
||||
* \param poly The polygon
|
||||
* \param transformed_startPoint The start point transformed such that it is
|
||||
* on the same horizontal line as the end point
|
||||
* \param transformed_endPoint The end point transformed such that it is on
|
||||
* the same horizontal line as the start point
|
||||
* \param transformation_matrix The transformation applied to the start and
|
||||
* end point to be applied to the polygon(s)
|
||||
* \return whether the line segment collides with the boundary of the
|
||||
* polygon(s)
|
||||
*/
|
||||
static bool polygonCollidesWithlineSegment(Polygons& polys, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix);
|
||||
|
||||
/*!
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
*
|
||||
* \param poly The polygon
|
||||
* \param startPoint The start point
|
||||
* \param endPoint The end point
|
||||
* \return whether the line segment collides with the boundary of the polygon(s)
|
||||
*/
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
*
|
||||
* If the line segment doesn't intersect with any edge of the polygon, but
|
||||
* merely touches it, a collision is also reported. For instance, a
|
||||
* collision is reported when the an endpoint of the line is exactly on the
|
||||
* polygon, and when the line coincides with an edge.
|
||||
*
|
||||
* \param poly The polygon
|
||||
* \param startPoint The start point
|
||||
* \param endPoint The end point
|
||||
* \return whether the line segment collides with the boundary of the
|
||||
* polygon(s)
|
||||
*/
|
||||
static bool polygonCollidesWithlineSegment(Polygons& polys, Point& startPoint, Point& endPoint);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#ifndef WEAVE_DATA_STORAGE_H
|
||||
#define WEAVE_DATA_STORAGE_H
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "mesh.h"
|
||||
@@ -69,7 +70,7 @@ struct WeaveLayer : WeaveConnection
|
||||
// [connections] are the vertical connections
|
||||
WeaveRoof roofs; //!< parts which are filled horizontally (both roofs and floors...)
|
||||
};
|
||||
struct WireFrame
|
||||
struct WireFrame : public NoCopy
|
||||
{
|
||||
MeshGroup* meshgroup;
|
||||
WeaveRoof bottom_infill;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include "GCodePlannerTest.h"
|
||||
|
||||
#include "../src/MeshGroup.h" //Needed to construct the GCodePlanner.
|
||||
|
||||
#define ALLOWED_ESTIMATE_ERROR 0.1 //Fraction of the time estimates that the estimate is allowed to be off from the ground truth.
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(GCodePlannerTest);
|
||||
|
||||
void GCodePlannerTest::setUp()
|
||||
{
|
||||
storage = new SliceDataStorage(nullptr); //Empty data.
|
||||
storage->retraction_config.speed = 1; //We'll set some of the configurations, just in order to get valid g-code (speed not zero, etc.)
|
||||
storage->retraction_config.primeSpeed = 1;
|
||||
storage->retraction_config.distance = 10;
|
||||
storage->travel_config.init(1,1,1);
|
||||
FanSpeedLayerTimeSettings fan_speed_layer_time_settings; //A dummy fan speed and layer time settings.
|
||||
fan_speed_layer_time_settings.cool_min_layer_time = 0;
|
||||
fan_speed_layer_time_settings.cool_min_layer_time_fan_speed_max = 1;
|
||||
fan_speed_layer_time_settings.cool_fan_speed_min = 0;
|
||||
fan_speed_layer_time_settings.cool_fan_speed_max = 1;
|
||||
fan_speed_layer_time_settings.cool_min_speed = 0.5;
|
||||
// Command Slice layer z layer last current fan speed and layer retraction comb travel travel avoid
|
||||
// socket storage nr height position extruder time settings combing offset avoid distance
|
||||
gCodePlanner = new GCodePlanner(nullptr, *storage, 0, 0, 0.1, Point(0,0), 0, fan_speed_layer_time_settings, false, 100, false, 50 );
|
||||
}
|
||||
|
||||
void GCodePlannerTest::tearDown()
|
||||
{
|
||||
delete gCodePlanner;
|
||||
gCodePlanner = nullptr;
|
||||
delete storage;
|
||||
storage = nullptr;
|
||||
}
|
||||
|
||||
void GCodePlannerTest::computeNaiveTimeEstimatesRetractionTest()
|
||||
{
|
||||
TimeMaterialEstimates estimate_empty = gCodePlanner->computeNaiveTimeEstimates(); //First try estimating time and material without any content.
|
||||
TimeMaterialEstimates estimate_empty_expected(0,0,0,0); //We expect the estimate of all time and material used to be 0.
|
||||
verifyEstimates(estimate_empty,estimate_empty_expected,"Empty GCodePlanner");
|
||||
|
||||
GCodeExport gcode;
|
||||
GCodePathConfig configuration = storage->travel_config;
|
||||
gCodePlanner->addExtrusionMove(Point(0,0),&configuration,1.0f); //Need to have at least one path to have a configuration.
|
||||
TimeMaterialEstimates before_retract = gCodePlanner->computeNaiveTimeEstimates();
|
||||
gCodePlanner->writeRetraction(gcode,(unsigned int)0,(unsigned int)0); //Make a retract.
|
||||
TimeMaterialEstimates after_retract = gCodePlanner->computeNaiveTimeEstimates();
|
||||
TimeMaterialEstimates estimate_one_retraction = after_retract - before_retract;
|
||||
double retract_unretract_time = configuration.retraction_config->distance / configuration.retraction_config->primeSpeed;
|
||||
TimeMaterialEstimates estimate_one_retraction_expected(0,retract_unretract_time * 0.5,retract_unretract_time * 0.5,0);
|
||||
verifyEstimates(estimate_one_retraction,estimate_one_retraction_expected,"One retraction");
|
||||
}
|
||||
|
||||
void GCodePlannerTest::verifyEstimates(const TimeMaterialEstimates& observed,const TimeMaterialEstimates& expected,std::string test_description)
|
||||
{
|
||||
//Check each of the four estimates in the TimeMaterialEstimate instances.
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << test_description << ": Extrude time is " << observed.getExtrudeTime() << " instead of the expected " << expected.getExtrudeTime();
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(),observed.getExtrudeTime() - expected.getExtrudeTime() < expected.getExtrudeTime() * ALLOWED_ESTIMATE_ERROR + 0.001);
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << test_description << ": Unretracted travel time is " << (observed.getTotalUnretractedTime() - observed.getExtrudeTime()) << " instead of the expected " << (expected.getTotalUnretractedTime() - expected.getExtrudeTime());
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(),observed.getTotalUnretractedTime() - observed.getExtrudeTime() - (expected.getTotalUnretractedTime() - expected.getExtrudeTime()) < (expected.getTotalUnretractedTime() - expected.getExtrudeTime()) * ALLOWED_ESTIMATE_ERROR + 0.001);
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << test_description << ": Retracted travel time is " << (observed.getTotalTime() - observed.getTotalUnretractedTime()) << " instead of the expected " << (expected.getTotalTime() - expected.getTotalUnretractedTime());
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(),observed.getTotalTime() - observed.getTotalUnretractedTime() - (expected.getTotalTime() - expected.getTotalUnretractedTime()) < (expected.getTotalTime() - expected.getTotalUnretractedTime()) * ALLOWED_ESTIMATE_ERROR + 0.001);
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << test_description << ": Material used is " << observed.getMaterial() << " instead of the expected " << expected.getMaterial();
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(),observed.getMaterial() - expected.getMaterial() < expected.getMaterial() * ALLOWED_ESTIMATE_ERROR + 0.001);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#ifndef GCODEPLANNERTEST_H
|
||||
#define GCODEPLANNERTEST_H
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include "../src/gcodePlanner.h"
|
||||
#include "../src/sliceDataStorage.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class GCodePlannerTest : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(GCodePlannerTest);
|
||||
CPPUNIT_TEST(computeNaiveTimeEstimatesRetractionTest);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/*!
|
||||
* \brief Sets up the test suite to prepare for testing.
|
||||
*
|
||||
* This creates an instance of <em>gcodePlanner</em>, ready for filling with
|
||||
* data.
|
||||
*/
|
||||
void setUp();
|
||||
|
||||
/*!
|
||||
* \brief Tears down the test suite when testing is done.
|
||||
*
|
||||
* This destroys the <em>gcodePlanner</em> instance.
|
||||
*/
|
||||
void tearDown();
|
||||
|
||||
//The actual test cases.
|
||||
void computeNaiveTimeEstimatesRetractionTest();
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief The instance of gcodePlanner that can be used for testing.
|
||||
*
|
||||
* This instance is re-created for each test. Between tests it will be
|
||||
* <em>nullptr</em>.
|
||||
*/
|
||||
GCodePlanner* gCodePlanner;
|
||||
|
||||
/*!
|
||||
* \brief Slice data storage to construct the <em>GCodePlanner</em> with.
|
||||
*
|
||||
* It also holds configurations for the paths to add to the
|
||||
* <em>GCodePlanner</em>.
|
||||
*/
|
||||
SliceDataStorage* storage;
|
||||
|
||||
/*!
|
||||
* \brief Asserts that the two time material estimates are equal.
|
||||
*
|
||||
* If they are not equal, the error messages are formulated according to the
|
||||
* specified observed results versus the specified expected results. It will
|
||||
* include a specified string describing what test it was.
|
||||
*
|
||||
* \param observed The observed time material estimates.
|
||||
* \param expected The expected (true) time material estimates.
|
||||
* \param test_description A description of the test that was performed to
|
||||
* get the observed results.
|
||||
*/
|
||||
void verifyEstimates(const TimeMaterialEstimates& observed,const TimeMaterialEstimates& expected,std::string test_description);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // GCODEPLANNERTEST_H
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include "LinearAlg2DTest.h"
|
||||
|
||||
#include <../src/utils/linearAlg2D.h>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(LinearAlg2DTest);
|
||||
|
||||
void LinearAlg2DTest::setUp()
|
||||
{
|
||||
//Do nothing.
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::tearDown()
|
||||
{
|
||||
//Do nothing.
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalNearTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(25,3),9,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalOnTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(25,0),0,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalBeyondTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(200,0),10000,1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalBeforeTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(-100,0),10000,-1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalCornerTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(-1,-1),2,-1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalPerpendicularTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(0,3),9,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentVerticalNearTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(5,25),25,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentVerticalOnTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(0,25),0,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentVerticalBeyondTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(0,200),10000,1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentVerticalBeforeTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(0,-100),10000,-1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentVerticalCornerTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(-1,-1),2,-1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentVerticalPerpendicularTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(3,0),9,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalNearTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(30,20),50,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalOnTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(25,25),0,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalBeyondTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(200,200),20000,1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalBeforeTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(-100,-100),20000,-1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalCornerTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(-3,0),9,-1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalPerpendicularTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(3,-3),9,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2NearTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(20,30),320,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2OnTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(40,20),0,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2PointTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(0,0),0,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2BeyondTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(200,100),12500,1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2BeforeTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(-100,-50),12500,-1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2CornerTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(-3,0),9,-1);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2PerpendicularTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(-2,4),20,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2LargeTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(10000,5000),Point(2000,3000),3200000,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentZeroNearTest()
|
||||
{
|
||||
int64_t supposed_distance = LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(20,0),Point(0,0));
|
||||
int64_t actual_distance = 400;
|
||||
std::stringstream ss;
|
||||
ss << "Line [0,0] -- [0,0], point [20,0], squared distance was ";
|
||||
ss << supposed_distance;
|
||||
ss << " rather than ";
|
||||
ss << actual_distance;
|
||||
ss << ".";
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(),std::abs(supposed_distance - actual_distance) <= maximum_error);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentZeroOnTest()
|
||||
{
|
||||
getDist2FromLineSegmentAssert(Point(0,0),Point(0,0),Point(0,0),0,0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentAssert(Point line_start,Point line_end,Point point,int64_t actual_distance2,char actual_is_beyond)
|
||||
{
|
||||
char supposed_is_beyond;
|
||||
int64_t supposed_distance = LinearAlg2D::getDist2FromLineSegment(line_start,point,line_end,&supposed_is_beyond);
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Line [";
|
||||
ss << line_start.X;
|
||||
ss << ",";
|
||||
ss << line_start.Y;
|
||||
ss << "] -- [";
|
||||
ss << line_end.X;
|
||||
ss << ",";
|
||||
ss << line_end.Y;
|
||||
ss << "], point [";
|
||||
ss << point.X;
|
||||
ss << ",";
|
||||
ss << point.Y;
|
||||
ss << "], squared distance was ";
|
||||
ss << supposed_distance;
|
||||
ss << " rather than ";
|
||||
ss << actual_distance2;
|
||||
ss << ".";
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(),std::fabs(sqrt(double(supposed_distance)) - sqrt(double(actual_distance2))) <= maximum_error);
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Line [";
|
||||
ss << line_start.X;
|
||||
ss << ",";
|
||||
ss << line_start.Y;
|
||||
ss << "] -- [";
|
||||
ss << line_end.X;
|
||||
ss << ",";
|
||||
ss << line_end.Y;
|
||||
ss << "], point [";
|
||||
ss << point.X;
|
||||
ss << ",";
|
||||
ss << point.Y;
|
||||
ss << "], check whether it is beyond was ";
|
||||
ss << static_cast<int>(supposed_is_beyond);
|
||||
ss << " rather than ";
|
||||
ss << static_cast<int>(actual_is_beyond);
|
||||
ss << ".";
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(),supposed_is_beyond == actual_is_beyond);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#ifndef LINEARALG2DTEST_H
|
||||
#define LINEARALG2DTEST_H
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <../src/utils/intpoint.h>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class LinearAlg2DTest : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(LinearAlg2DTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalNearTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalOnTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalBeyondTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalBeforeTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalCornerTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalPerpendicularTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentVerticalNearTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentVerticalOnTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentVerticalBeyondTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentVerticalBeforeTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentVerticalCornerTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentVerticalPerpendicularTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalNearTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalOnTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalBeyondTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalBeforeTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalCornerTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalPerpendicularTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2NearTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2OnTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2PointTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2BeyondTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2BeforeTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2CornerTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2PerpendicularTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2LargeTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentZeroNearTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentZeroOnTest);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/*!
|
||||
* \brief Sets up the test suite to prepare for testing.
|
||||
*
|
||||
* Since <em>LinearAlg2DTest</em> only has static functions, no instance
|
||||
* needs to be created here.
|
||||
*/
|
||||
void setUp();
|
||||
|
||||
/*!
|
||||
* \brief Tears down the test suite when testing is done.
|
||||
*
|
||||
* Since <em>LinearAlg2DTest</em> only has static functions, no instance
|
||||
* exists that needs to be destroyed.
|
||||
*/
|
||||
void tearDown();
|
||||
|
||||
//These are the actual test cases. The name of the function sort of describes what it tests but I refuse to document all of these, sorry.
|
||||
void getDist2FromLineSegmentHorizontalNearTest();
|
||||
void getDist2FromLineSegmentHorizontalOnTest();
|
||||
void getDist2FromLineSegmentHorizontalBeyondTest();
|
||||
void getDist2FromLineSegmentHorizontalBeforeTest();
|
||||
void getDist2FromLineSegmentHorizontalCornerTest();
|
||||
void getDist2FromLineSegmentHorizontalPerpendicularTest();
|
||||
void getDist2FromLineSegmentVerticalNearTest();
|
||||
void getDist2FromLineSegmentVerticalOnTest();
|
||||
void getDist2FromLineSegmentVerticalBeyondTest();
|
||||
void getDist2FromLineSegmentVerticalBeforeTest();
|
||||
void getDist2FromLineSegmentVerticalCornerTest();
|
||||
void getDist2FromLineSegmentVerticalPerpendicularTest();
|
||||
void getDist2FromLineSegmentDiagonalNearTest();
|
||||
void getDist2FromLineSegmentDiagonalOnTest();
|
||||
void getDist2FromLineSegmentDiagonalBeyondTest();
|
||||
void getDist2FromLineSegmentDiagonalBeforeTest();
|
||||
void getDist2FromLineSegmentDiagonalCornerTest();
|
||||
void getDist2FromLineSegmentDiagonalPerpendicularTest();
|
||||
void getDist2FromLineSegmentDiagonal2NearTest();
|
||||
void getDist2FromLineSegmentDiagonal2OnTest();
|
||||
void getDist2FromLineSegmentDiagonal2PointTest();
|
||||
void getDist2FromLineSegmentDiagonal2BeyondTest();
|
||||
void getDist2FromLineSegmentDiagonal2BeforeTest();
|
||||
void getDist2FromLineSegmentDiagonal2CornerTest();
|
||||
void getDist2FromLineSegmentDiagonal2PerpendicularTest();
|
||||
void getDist2FromLineSegmentDiagonal2LargeTest();
|
||||
void getDist2FromLineSegmentZeroNearTest();
|
||||
void getDist2FromLineSegmentZeroOnTest();
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief The maximum allowed error in distance measurements.
|
||||
*/
|
||||
static const int64_t maximum_error = 10;
|
||||
|
||||
/*!
|
||||
* \brief Performs the actual assertion for the getDist2FromLineSegmentTest.
|
||||
*
|
||||
* This is essentially a parameterised version of all unit tests pertaining
|
||||
* to the getDist2FromLineSegment tests.
|
||||
*
|
||||
* \param line_start The start of the line to check the distance to.
|
||||
* \param line_end The end of the line to check the distance to.
|
||||
* \param point The point to check the distance to the line with.
|
||||
* \param actual_distance2 The correct distance from the point to the line,
|
||||
* squared.
|
||||
* \param actual_is_beyond Whether the point is actually beyond the line.
|
||||
*/
|
||||
void getDist2FromLineSegmentAssert(Point line_start,Point line_end,Point point,int64_t actual_distance2,char actual_is_beyond);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LINEARALG2DTEST_H
|
||||
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Arquivo binário não exibido.
Arquivo binário não exibido.
@@ -0,0 +1,32 @@
|
||||
Regression test:
|
||||
---------------
|
||||
- Slice on old Cura (15.04 and older versions of the new Cura)
|
||||
- Slice on the current version of Cura
|
||||
- Print and compare printed results
|
||||
|
||||
Compatibility test:
|
||||
------------------
|
||||
- Slice on current version of Cura on your own development system
|
||||
- Slice on current version from build on various operating systems
|
||||
- diff on gcode to see whether they are the same
|
||||
|
||||
|
||||
==================================================================================
|
||||
|
||||
|
||||
|
||||
How to slice:
|
||||
- Load default normal quality settings
|
||||
- Perform exceptions to above rule (listed below)
|
||||
- Clear build platform
|
||||
- Load model
|
||||
- Don't move or resize model!
|
||||
- Let it slice...
|
||||
|
||||
Exceptions:
|
||||
3DHackerTest.stl no support
|
||||
ctrlV_3D_test.stl no support
|
||||
Debailey_x10.stl
|
||||
dragon_65_tilted_large.stl
|
||||
TortureTestV2.stl no support
|
||||
UltimakerRobot_support_2015.stl no support
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
@@ -0,0 +1,25 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//UltiScanTastic is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include <cppunit/extensions/TestFactoryRegistry.h>
|
||||
#include <cppunit/ui/text/TestRunner.h>
|
||||
#include <cppunit/XmlOutputter.h>
|
||||
|
||||
/*!
|
||||
* \brief Runs the test cases.
|
||||
*/
|
||||
int main(int argc,char** argv)
|
||||
{
|
||||
CppUnit::TextUi::TestRunner runner;
|
||||
|
||||
//Set the output type to be JUnit-style XML.
|
||||
std::ofstream output("output.xml");
|
||||
CppUnit::XmlOutputter* outputter = new CppUnit::XmlOutputter(&runner.result(), output);
|
||||
runner.setOutputter(outputter);
|
||||
|
||||
CppUnit::TestFactoryRegistry& registry = CppUnit::TestFactoryRegistry::getRegistry();
|
||||
runner.addTest(registry.makeTest()); //Makes a test suite from all test cases that are registered with CPPUNIT_TEST_SUITE_REGISTRATION().
|
||||
bool success = runner.run("", false); //Run the tests!
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
||||
+268
-20
@@ -1,28 +1,276 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/python3
|
||||
|
||||
## runtest.py
|
||||
# The runtest.py script runs regression tests on the CuraEngine.
|
||||
# It parses the json file for settings to know which settings can be passed towards the engine.
|
||||
# It runs the following test:
|
||||
# * Defaults
|
||||
# * Bounds/possible values
|
||||
# * Single random value
|
||||
# * All settings random
|
||||
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
import time
|
||||
import stat
|
||||
import argparse
|
||||
import random
|
||||
import json
|
||||
import threading
|
||||
from xml.etree import ElementTree
|
||||
|
||||
def main(engine, model_path):
|
||||
filenames = sorted(os.listdir(model_path), key=lambda filename: os.stat(os.path.join(model_path, filename)).st_size)
|
||||
filenames = list(filter(lambda filename: filename.lower().endswith('.stl'), filenames))
|
||||
for filename in filenames:
|
||||
print("Slicing: %s (%d/%d)" % (filename, filenames.index(filename), len(filenames)))
|
||||
t = time.time()
|
||||
p = subprocess.Popen([engine, '-vv', os.path.join(model_path, filename)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
if p.wait() != 0:
|
||||
print ("Engine failed to report success on test object: %s" % (filename))
|
||||
print(stderr.decode('utf-8', 'replace').split('\n')[-5:])
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("Slicing took: %f" % (time.time() - t))
|
||||
|
||||
if __name__ == '__main__':
|
||||
model_path = 'tests'
|
||||
if len(sys.argv) > 2:
|
||||
model_path = sys.argv[2]
|
||||
main(sys.argv[1], model_path)
|
||||
## The TestSuite class stores the test results of a single set of tests.
|
||||
# TestSuite objects are created by the TestResults class.
|
||||
class TestSuite():
|
||||
def __init__(self, name):
|
||||
self._name = name
|
||||
self._successes = []
|
||||
self._failures = []
|
||||
|
||||
## Add a successfull test result to the test suite.
|
||||
def success(self, class_name, test_name):
|
||||
#print('Success:', class_name, test_name)
|
||||
self._successes.append((class_name, test_name))
|
||||
|
||||
## Add a failed test result to the test suite.
|
||||
def failure(self, class_name, test_name, error_message):
|
||||
#print('Failure:', class_name, test_name, error_message)
|
||||
self._failures.append((class_name, test_name, error_message))
|
||||
|
||||
## Return the number of tests in this test suite
|
||||
def getTestCount(self):
|
||||
return self.getSuccessCount() + self.getFailureCount()
|
||||
|
||||
## Return the number of successfull tests in this test suite
|
||||
def getSuccessCount(self):
|
||||
return len(self._successes)
|
||||
|
||||
## Return the number of failed tests in this test suite
|
||||
def getFailureCount(self):
|
||||
return len(self._failures)
|
||||
|
||||
|
||||
## The TestResults class stores a group of TestSuite objects, each TestSuite object contains failed and successful test.
|
||||
# This class can output the result of the tests in a JUnit xml format for parsing in Jenkins.
|
||||
class TestResults():
|
||||
def __init__(self):
|
||||
self._testsuites = []
|
||||
|
||||
## Create a new test suite with the name.
|
||||
def addTestSuite(self, name):
|
||||
suite = TestSuite(name)
|
||||
self._testsuites.append(suite)
|
||||
return suite
|
||||
|
||||
def getFailureCount(self):
|
||||
result = 0
|
||||
for testsuite in self._testsuites:
|
||||
result += testsuite.getFailureCount()
|
||||
return result
|
||||
|
||||
## Save the test results to the file given in the filename.
|
||||
def saveXML(self, filename):
|
||||
xml = ElementTree.Element("testsuites")
|
||||
xml.text = "\n"
|
||||
for testsuite in self._testsuites:
|
||||
testsuite_xml = ElementTree.SubElement(xml, "testsuite", {"name": testsuite._name, "errors": "0", "tests": str(testsuite.getTestCount()), "failures": str(testsuite.getFailureCount()), "time": "0", "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())})
|
||||
testsuite_xml.text = "\n"
|
||||
testsuite_xml.tail = "\n"
|
||||
for class_name, test_name in testsuite._successes:
|
||||
testcase_xml = ElementTree.SubElement(testsuite_xml, "testcase", {"classname": class_name, "name": test_name})
|
||||
testcase_xml.text = "\n"
|
||||
testcase_xml.tail = "\n"
|
||||
for class_name, test_name, error_message in testsuite._failures:
|
||||
testcase_xml = ElementTree.SubElement(testsuite_xml, "testcase", {"classname": class_name, "name": test_name})
|
||||
testcase_xml.text = "\n"
|
||||
testcase_xml.tail = "\n"
|
||||
failure = ElementTree.SubElement(testcase_xml, "failure", {"message": "test failure"})
|
||||
failure.text = error_message
|
||||
failure.tail = "\n"
|
||||
return ElementTree.ElementTree(xml).write(filename, "utf-8", True)
|
||||
|
||||
|
||||
class Setting():
|
||||
def __init__(self, key, data):
|
||||
self._key = key
|
||||
self._default = data["default"]
|
||||
self._type = data["type"]
|
||||
self._min_value = data.get("min_value", None)
|
||||
self._max_value = data.get("max_value", None)
|
||||
self._min_value_warning = data.get("min_value_warning", None)
|
||||
self._max_value_warning = data.get("max_value_warning", None)
|
||||
self._options = data.get("options", None)
|
||||
if self._options is not None:
|
||||
self._options = list(self._options.keys())
|
||||
|
||||
## Return a list of possible values for this setting. This list depends on the setting type.
|
||||
# For number values it contains the minimal and maximal values.
|
||||
# For enums and booleans it will contain the exact possible values.
|
||||
# For string settings only the default value is returned.
|
||||
def getSettingValues(self):
|
||||
if self._type == "boolean":
|
||||
return ["True", "False"]
|
||||
if self._type == "float" or self._type == "int":
|
||||
ret = [self._default]
|
||||
if self._min_value is not None:
|
||||
ret.append(self._min_value)
|
||||
else:
|
||||
ret.append(-2)
|
||||
if self._min_value_warning is not None:
|
||||
ret.append(self._min_value_warning)
|
||||
if self._max_value is not None:
|
||||
ret.append(self._max_value)
|
||||
else:
|
||||
if self._max_value_warning is None:
|
||||
ret.append(10000)
|
||||
elif self._type == "float":
|
||||
ret.append(float(self._max_value_warning) + 100)
|
||||
ret.append(float(self._max_value_warning) * 10)
|
||||
elif self._type == "int":
|
||||
ret.append(int(self._max_value_warning) + 100)
|
||||
ret.append(int(self._max_value_warning) * 10)
|
||||
# If the type is boolean, string or enum, the warning values make no sense anyway, so don't test them.
|
||||
if self._max_value_warning is not None:
|
||||
ret.append(self._max_value_warning)
|
||||
|
||||
if self._type == "int":
|
||||
for n in range(0, len(ret)):
|
||||
ret[n] = int(ret[n])
|
||||
return ret
|
||||
if self._type == "enum":
|
||||
return self._options
|
||||
if self._type == "string":
|
||||
return self._default
|
||||
print("Unknown setting type:", self._type)
|
||||
|
||||
## Return a random value for this setting. The returned value will be a valid value according to the settings json file.
|
||||
def getRandomValue(self):
|
||||
if self._type == "float" or self._type == "int":
|
||||
min = -2
|
||||
if self._min_value_warning is not None:
|
||||
min = self._min_value_warning
|
||||
if self._min_value is not None:
|
||||
min = self._min_value
|
||||
max = 10000
|
||||
if self._max_value_warning is not None:
|
||||
max = self._max_value_warning
|
||||
if self._max_value is not None:
|
||||
max = self._max_value
|
||||
if self._type == "int":
|
||||
return random.randint(int(min), int(max))
|
||||
return random.uniform(float(min), float(max))
|
||||
return random.choice(self.getSettingValues())
|
||||
|
||||
class EngineTest():
|
||||
def __init__(self, json_filename, engine_filename, models):
|
||||
self._json_filename = json_filename
|
||||
self._json = json.load(open(json_filename, "r"))
|
||||
self._engine = engine_filename
|
||||
self._models = models
|
||||
self._settings = {}
|
||||
self._test_results = TestResults()
|
||||
|
||||
self._flattenAllSettings()
|
||||
|
||||
def _flattenAllSettings(self):
|
||||
for key, data in self._json["categories"].items():
|
||||
self._flattenSettings(data["settings"])
|
||||
|
||||
def _flattenSettings(self, settings):
|
||||
for key, setting in settings.items():
|
||||
self._settings[key] = Setting(key, setting)
|
||||
if "children" in setting:
|
||||
self._flattenSettings(setting["children"])
|
||||
|
||||
def testDefaults(self):
|
||||
suite = self._test_results.addTestSuite("Defaults")
|
||||
self._runTest(suite, "defaults", {})
|
||||
return suite.getFailureCount()
|
||||
|
||||
def testSingleChanges(self):
|
||||
suite = self._test_results.addTestSuite("SingleSetting")
|
||||
for key, setting in self._settings.items():
|
||||
for value in setting.getSettingValues():
|
||||
self._runTest(suite, key, {key: value})
|
||||
return suite.getFailureCount()
|
||||
|
||||
def testSingleRandom(self):
|
||||
suite = self._test_results.addTestSuite("SingleRandom")
|
||||
for key, setting in self._settings.items():
|
||||
self._runTest(suite, key, {key: setting.getRandomValue()})
|
||||
return suite.getFailureCount()
|
||||
|
||||
def testDualRandom(self):
|
||||
suite = self._test_results.addTestSuite("DualRandom")
|
||||
for key, setting in self._settings.items():
|
||||
for key2, setting2 in self._settings.items():
|
||||
if key != key2:
|
||||
self._runTest(suite, key, {key: setting.getRandomValue(), key2: setting2.getRandomValue()})
|
||||
return suite.getFailureCount()
|
||||
|
||||
def testAllRandom(self, amount):
|
||||
suite = self._test_results.addTestSuite("AllRandom_%d" % (amount))
|
||||
for n in range(0, amount):
|
||||
settings = {}
|
||||
for key, setting in self._settings.items():
|
||||
settings[key] = setting.getRandomValue()
|
||||
self._runTest(suite, "Random", settings)
|
||||
return suite.getFailureCount()
|
||||
|
||||
def _runTest(self, suite, class_name, settings):
|
||||
test_name = ', '.join("{!s}={!r}".format(key, val) for (key,val) in settings.items())
|
||||
for model in self._models:
|
||||
this_test_name = '%s.%s' % (os.path.basename(model), test_name)
|
||||
cmd = [self._engine, "slice", "-j", self._json_filename, "-o", "/dev/null"]
|
||||
for key, value in settings.items():
|
||||
cmd += ['-s', '%s=%s' % (key, value)]
|
||||
cmd += ["-l", model]
|
||||
error = self._runProcess(cmd)
|
||||
if error is not None:
|
||||
suite.failure(class_name, this_test_name, error)
|
||||
else:
|
||||
suite.success(class_name, this_test_name)
|
||||
|
||||
def _runProcess(self, cmd):
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
p.error = ""
|
||||
t = threading.Thread(target=self._abortProcess, args=(p,))
|
||||
t.start()
|
||||
stdout, stderr = p.communicate()
|
||||
if p.error == "Timeout":
|
||||
return "Timeout: %s" % (' '.join(cmd))
|
||||
if p.wait() != 0:
|
||||
return "Execution failed: %s" % (' '.join(cmd))
|
||||
return None
|
||||
|
||||
def _abortProcess(self, p):
|
||||
time.sleep(60)
|
||||
if p.poll() is None:
|
||||
p.terminate()
|
||||
p.error = "Timeout"
|
||||
|
||||
def getResults(self):
|
||||
return self._test_results
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="CuraEngine testing script")
|
||||
parser.add_argument("--simple", action="store_true", help="Only run the single test, exit")
|
||||
parser.add_argument("json", type=str, help="Machine JSON file to use")
|
||||
parser.add_argument("engine", type=str, help="Engine executable")
|
||||
parser.add_argument("models", type=str, nargs="+", help="List of models to use for testing")
|
||||
args = parser.parse_args()
|
||||
|
||||
et = EngineTest(args.json, args.engine, args.models)
|
||||
if et.testDefaults() == 0:
|
||||
if not args.simple:
|
||||
et.testSingleChanges()
|
||||
if et.testSingleRandom() == 0:
|
||||
et.testDualRandom()
|
||||
if et.testAllRandom(10) == 0:
|
||||
et.testAllRandom(100)
|
||||
et.getResults().saveXML("output.xml")
|
||||
if args.simple:
|
||||
if et.getResults().getFailureCount() > 0:
|
||||
sys.exit(1)
|
||||
|
||||
+19658
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Referência em uma Nova Issue
Bloquear um usuário