Comparar commits
502 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| ecd832744c | |||
| 03b654af3e | |||
| c79a7f1819 | |||
| e01f18c7d4 | |||
| 42891874f4 | |||
| 257d6a6635 | |||
| bec8bef455 | |||
| 4efeaa7083 | |||
| 95fd9d6685 | |||
| 2db37c6018 | |||
| 41b0966d26 | |||
| 93485cd0df | |||
| 2d3382874a | |||
| 1e78397e18 | |||
| 6bcdd94f7e | |||
| accd28db64 | |||
| 1c0f4c42d9 | |||
| 5da1632d9f | |||
| cbf1152f56 | |||
| 2273c5aefe | |||
| 46c793e73d | |||
| fd4969887b | |||
| 6620a050a5 | |||
| 6325197fce | |||
| f828d44365 | |||
| 29564a23e0 | |||
| 1fdda3319f | |||
| be113eceb4 | |||
| 0c42ff9bfa | |||
| 168e041c42 | |||
| 670ae6dd8c | |||
| 20adfa751f | |||
| bf8776b112 | |||
| 1d0f3f519a | |||
| 07fef8668c | |||
| 1c06fc49fc | |||
| beb9422d9b | |||
| a8359b9a68 | |||
| 5ccfe2d1aa | |||
| 74577759b4 | |||
| 45eb026777 | |||
| aabb07fd81 | |||
| 6377ec63e1 | |||
| eab2d8e667 | |||
| 8d41003c67 | |||
| ecfae4d75c | |||
| a2208f6b69 | |||
| bacacb01dc | |||
| 94c9399f2c | |||
| 168dc3c12b | |||
| 235af65b00 | |||
| f3f3be74cc | |||
| dca0bc80b5 | |||
| c0e57622d0 | |||
| b7a8fbe798 | |||
| 4353980e78 | |||
| 18ae9cf41d | |||
| 04edf35331 | |||
| 6718a8b2f3 | |||
| 08a5ec7dee | |||
| 3de01763c1 | |||
| bc7ee74d19 | |||
| fc20a6661b | |||
| 81d521a58b | |||
| c4eb1d9f27 | |||
| 32d1bb6d75 | |||
| afdb552f63 | |||
| a400ba28f2 | |||
| e0a7818d9e | |||
| 277b5dce75 | |||
| 462a6e8c16 | |||
| c20d35e293 | |||
| 9e56841cd2 | |||
| c39e43c161 | |||
| 421a6d4095 | |||
| 8497e46542 | |||
| 6510ebbd92 | |||
| c1b4a5398b | |||
| 11c4b9339a | |||
| 75efbac68e | |||
| cd199dc43e | |||
| 2372a78c9b | |||
| 142f4d519f | |||
| 9ea43e7fc1 | |||
| 9b92de9b8b | |||
| 6826581497 | |||
| 4fead4612b | |||
| f763edfb05 | |||
| 0e662d7d67 | |||
| 94d8c3ff32 | |||
| 8e91753afc | |||
| 21e59cc1e2 | |||
| f0f14b0be3 | |||
| a82c00bead | |||
| 14b1c5333a | |||
| bbe809dabc | |||
| ed581a92b2 | |||
| 9b68305851 | |||
| 940d3a86bd | |||
| 55047120d8 | |||
| e50b00fd73 | |||
| 7377f30fd0 | |||
| c9245e0926 | |||
| 7149b7dbf1 | |||
| daa3927a99 | |||
| 074d1125e6 | |||
| a92274678d | |||
| 081393d7f0 | |||
| 30da3097d0 | |||
| d830fc515b | |||
| 5f2d924771 | |||
| b1ab1cae5a | |||
| 1fb6b20d90 | |||
| dfb4c98e35 | |||
| e455d63ad2 | |||
| cb024c73b1 | |||
| d2d25058cb | |||
| e85362370b | |||
| 00c6f5c092 | |||
| 818c7da951 | |||
| 41723c8b38 | |||
| c8d75dd913 | |||
| faa60c408f | |||
| 7e322bc57c | |||
| 888fc54660 | |||
| fd660fcc11 | |||
| e8080422a4 | |||
| 5fecf2cd17 | |||
| b4bf17c6be | |||
| fabd658b53 | |||
| a2050de513 | |||
| 9bfb5b17e9 | |||
| e93d39d6d5 | |||
| 175a65415a | |||
| 74986dd95c | |||
| 55dd35ae5e | |||
| 8caf810642 | |||
| 76efc41407 | |||
| b0a0fe8f30 | |||
| 653b631ffd | |||
| b9a01ed031 | |||
| de9d3bb447 | |||
| 8f5a46f77d | |||
| c22793b5d7 | |||
| 1e684f23a7 | |||
| 7d0025975b | |||
| 5c892b564b | |||
| d5327ec3f1 | |||
| c48104bc86 | |||
| f58f1daec3 | |||
| 669bb523a3 | |||
| fa74f672ac | |||
| e224d9c853 | |||
| 338d80a7a9 | |||
| 87123dac31 | |||
| 73de288f44 | |||
| 9f5b5f405d | |||
| 2850b34227 | |||
| 072d320f1d | |||
| 8e4b61d2a5 | |||
| ba4e26f801 | |||
| cea0a0a98f | |||
| 2b51a11739 | |||
| d32443ca9b | |||
| 792aa9e8e8 | |||
| a63abb4e88 | |||
| 9f82f58f03 | |||
| df04467ab1 | |||
| bbd9412b5f | |||
| c476acd522 | |||
| 278efdeb39 | |||
| bb8a9dacba | |||
| 42420edeed | |||
| be2e96f3fd | |||
| 18adc0bbc9 | |||
| e7824faefe | |||
| 7199be1b4f | |||
| 727c863f1a | |||
| d732c49dd7 | |||
| 781fc5ed7b | |||
| 7243cf6da4 | |||
| 1cdcce4205 | |||
| acf381c008 | |||
| 4b7df9ddc0 | |||
| 7788a4a234 | |||
| 6224713998 | |||
| db8b30d77a | |||
| 050b9c88f2 | |||
| 8ac63fca6e | |||
| 1383882bc5 | |||
| 3a773d3c0f | |||
| 734ddce3c8 | |||
| 0b19936299 | |||
| 691d5de591 | |||
| 490cef1a5c | |||
| b777b55935 | |||
| 0428c08152 | |||
| 20c74dd22d | |||
| 188b190d21 | |||
| 1cd128decd | |||
| 925247a54d | |||
| 94d9a948ae | |||
| 176bf2c887 | |||
| 3cf208f8ed | |||
| 08eec2ad1f | |||
| 41e012be5f | |||
| 8102f7a246 | |||
| d527358a82 | |||
| 6115bdec2a | |||
| 9f29cbd3fb | |||
| 5c3727d532 | |||
| 73152d84c7 | |||
| c7ce43eb6c | |||
| d9e2b39baf | |||
| 34e86195e9 | |||
| bad780c54b | |||
| 3ddbc1d53b | |||
| 51757353b4 | |||
| d931baa788 | |||
| 321a9c11e0 | |||
| 389950ed90 | |||
| 61e5187267 | |||
| 3859d58571 | |||
| 345c599062 | |||
| 73625b949f | |||
| bf1cf334e6 | |||
| 13ec686608 | |||
| 8b244c1738 | |||
| 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 | |||
| 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 | |||
| 017fb03391 | |||
| 00abb3ab4c | |||
| dfc91d811f | |||
| 835424121f | |||
| 6866925a05 | |||
| 950441493c | |||
| 0c7cca6f17 | |||
| c058a2b12b | |||
| adc8341e49 | |||
| d6ed95ed81 | |||
| 102b9f9c75 | |||
| abb0b9a763 | |||
| 71ae42a949 | |||
| 49a1ed7f3b | |||
| 190b5b789b | |||
| a6be90d3b4 | |||
| 3231d75d94 | |||
| 5e4080e0bb | |||
| 6432215ad9 | |||
| 3a60d0cb01 | |||
| f1902d88bf | |||
| 4cc5c35564 | |||
| f124a7e528 | |||
| 1cd8e1c500 | |||
| 6dc89bbe7f | |||
| 16250c54c9 | |||
| c5be07212e | |||
| 7035e91ac6 | |||
| 4f972da7ae | |||
| 47718a1ba4 | |||
| 87db7232a1 | |||
| b9f9c12d6c | |||
| 046fa3ba8e | |||
| db039033ce | |||
| c0fa1b4a1a | |||
| a455913165 | |||
| 9b0e4be570 | |||
| f9a0e39a6a | |||
| ff9e7f11ae | |||
| f5e2c5a070 | |||
| 85cfd422ba | |||
| c5334e355e | |||
| 086c38ac0b | |||
| 4cb64bab67 | |||
| 87eaa97f21 | |||
| 59fc004f09 | |||
| 5dbfab6ffb | |||
| 5494ce12a5 | |||
| 1ed1de147a | |||
| 1c0a616ec7 | |||
| 82a4bff953 | |||
| f7bbda7599 | |||
| 273f1f46f3 | |||
| e81abe405d | |||
| d0dd97ab04 | |||
| d0e6f5c07e | |||
| f4eb33e14e | |||
| 3d3ddc238f | |||
| 704edd25af | |||
| e33560beb6 | |||
| 4ca66b6916 | |||
| 7e6c4661b9 | |||
| f96eaddf6c | |||
| 5c41425859 | |||
| 85959b0aa7 | |||
| 6bd1d9fe8c | |||
| 3e43df9e60 | |||
| faa580ffe6 | |||
| 16679d68d0 | |||
| 9983ebac7b | |||
| ee8ef2bfa5 | |||
| 7646426f9d | |||
| 2976b1276c | |||
| b84f41d9b0 | |||
| f142df9176 | |||
| 94f90419dd | |||
| 90547bb69c | |||
| 3611d53222 | |||
| ab7ca73596 | |||
| c42568c059 | |||
| 43eb784b6c | |||
| 68f1eb1fd2 | |||
| 6518e14e18 | |||
| 4c95e43048 | |||
| 4c66b7fd01 | |||
| 97ebfe8b12 | |||
| 0cb8b64f70 | |||
| bfc2d458ae | |||
| 365de0578c | |||
| d9c30de796 | |||
| bc41b415c2 | |||
| 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 | |||
| 1a7137a6c8 | |||
| 4c967ec6a3 | |||
| b455661a75 | |||
| 46b7d1ccab | |||
| fb95022776 | |||
| 62ce0b5513 | |||
| 12c25b2214 | |||
| 988af0f6ed | |||
| 53714319b4 | |||
| fe44537924 | |||
| a285b22804 | |||
| e588fe100b | |||
| ad81cc8a12 | |||
| a3db17442c | |||
| e0cf567bf7 | |||
| 904ad24eec | |||
| f49803b8a9 | |||
| 2b422ce6c0 | |||
| b2cc46a4cb | |||
| 98d105d73c | |||
| ecd32700f5 | |||
| 44c77d2a31 | |||
| 38e832c172 | |||
| 64ef6e5698 | |||
| 769dc252df | |||
| 7093231707 | |||
| ad93a8adde | |||
| 1bb8bde1de | |||
| d6822cdaf8 | |||
| 2817528c51 | |||
| a52f101c14 | |||
| 22b1d427ec | |||
| b89733320d | |||
| f662844d78 | |||
| 38a4e45771 | |||
| 9602616a6b | |||
| 0823f8faf5 | |||
| 33d63e8359 | |||
| a2881b83f7 | |||
| 1070d367e1 | |||
| e861fba11b | |||
| 226c974b5f | |||
| b25c72a9e3 | |||
| 6caf2b2db9 | |||
| b6138cb951 | |||
| e9b0cfc939 | |||
| f6e44af43d | |||
| 487f35abd4 | |||
| 2a163528f9 | |||
| 02e6c87396 | |||
| b367af6ad1 | |||
| eef8b77405 | |||
| 97a4c00022 | |||
| 6516ae0da9 |
+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
|
||||
|
||||
+25
-13
@@ -10,13 +10,19 @@ else()
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11")
|
||||
endif()
|
||||
|
||||
if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
|
||||
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("Building debug release of CuraEngine.")
|
||||
message(STATUS "Building debug release of CuraEngine.")
|
||||
add_definitions(-DASSERT_INSANE_OUTPUT)
|
||||
endif()
|
||||
|
||||
@@ -43,6 +49,7 @@ set(engine_SRCS # Except main.cpp.
|
||||
src/infill.cpp
|
||||
src/inset.cpp
|
||||
src/layerPart.cpp
|
||||
src/LayerPlanBuffer.cpp
|
||||
src/MergeInfillLines.cpp
|
||||
src/mesh.cpp
|
||||
src/MeshGroup.cpp
|
||||
@@ -63,7 +70,14 @@ set(engine_SRCS # Except main.cpp.
|
||||
src/Weaver.cpp
|
||||
src/Wireframe2gcode.cpp
|
||||
|
||||
src/infill/NoZigZagConnectorProcessor.cpp
|
||||
src/infill/ZigzagConnectorProcessorConnectedEndPieces.cpp
|
||||
src/infill/ZigzagConnectorProcessorDisconnectedEndPieces.cpp
|
||||
src/infill/ZigzagConnectorProcessorEndPieces.cpp
|
||||
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
|
||||
|
||||
src/utils/gettime.cpp
|
||||
src/utils/LinearAlg2D.cpp
|
||||
src/utils/logoutput.cpp
|
||||
src/utils/polygonUtils.cpp
|
||||
src/utils/polygon.cpp
|
||||
@@ -71,6 +85,7 @@ set(engine_SRCS # Except main.cpp.
|
||||
|
||||
# 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
|
||||
)
|
||||
|
||||
@@ -78,26 +93,24 @@ set(engine_TEST
|
||||
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
|
||||
|
||||
# Compiling CuraEngine itself.
|
||||
add_executable(CuraEngine ${engine_SRCS} src/main.cpp ${engine_PB_SRCS})
|
||||
target_link_libraries(CuraEngine clipper Arcus)
|
||||
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 Arcus)
|
||||
|
||||
set_target_properties(CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
|
||||
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)
|
||||
add_library(CuraEngineLibrary ${engine_SRCS} ${engine_PB_SRCS})
|
||||
if (UNIX)
|
||||
target_link_libraries(CuraEngineLibrary pthread)
|
||||
endif()
|
||||
message(STATUS "Building tests...")
|
||||
enable_testing()
|
||||
foreach (test ${engine_TEST})
|
||||
add_executable(${test} tests/main.cpp tests/${test}.cpp)
|
||||
target_link_libraries(${test} CuraEngineLibrary)
|
||||
target_link_libraries(${test} cppunit)
|
||||
target_link_libraries(${test} _CuraEngine cppunit)
|
||||
add_test(${test} ${test})
|
||||
endforeach()
|
||||
endif()
|
||||
@@ -105,5 +118,4 @@ endif()
|
||||
# Installing CuraEngine.
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
include(CPackConfig.cmake)
|
||||
|
||||
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)"
|
||||
|
||||
+6
-25
@@ -2,20 +2,18 @@ syntax = "proto3";
|
||||
|
||||
package cura.proto;
|
||||
|
||||
|
||||
message ObjectList
|
||||
message ObjectList
|
||||
{
|
||||
repeated Object objects = 1;
|
||||
repeated Setting settings = 2;
|
||||
}
|
||||
|
||||
// typeid 1
|
||||
message Slice
|
||||
{
|
||||
repeated ObjectList object_lists = 1;
|
||||
}
|
||||
|
||||
message Object
|
||||
message Object
|
||||
{
|
||||
int64 id = 1;
|
||||
bytes vertices = 2; //An array of 3 floats.
|
||||
@@ -24,28 +22,13 @@ message Object
|
||||
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
|
||||
}
|
||||
|
||||
// typeid 3
|
||||
message Progress
|
||||
message Progress
|
||||
{
|
||||
float amount = 1;
|
||||
}
|
||||
|
||||
// typeid 2
|
||||
message SlicedObjectList
|
||||
{
|
||||
repeated SlicedObject objects = 1;
|
||||
}
|
||||
|
||||
message SlicedObject
|
||||
{
|
||||
int64 id = 1;
|
||||
|
||||
repeated Layer layers = 2;
|
||||
}
|
||||
|
||||
message Layer {
|
||||
int32 id = 1;
|
||||
|
||||
float height = 2;
|
||||
float thickness = 3;
|
||||
|
||||
@@ -70,20 +53,16 @@ message Polygon {
|
||||
float line_width = 3;
|
||||
}
|
||||
|
||||
// typeid 4
|
||||
message GCodeLayer {
|
||||
int64 id = 1;
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
// typeid 5
|
||||
message ObjectPrintTime {
|
||||
int64 id = 1;
|
||||
float time = 2;
|
||||
float material_amount = 3;
|
||||
}
|
||||
|
||||
// typeid 6
|
||||
message SettingList {
|
||||
repeated Setting settings = 1;
|
||||
}
|
||||
@@ -94,7 +73,9 @@ message Setting {
|
||||
bytes value = 2;
|
||||
}
|
||||
|
||||
// typeid 7
|
||||
message GCodePrefix {
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
message SlicingFinished {
|
||||
}
|
||||
|
||||
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
|
||||
+289
-318
@@ -12,7 +12,7 @@ namespace cura
|
||||
|
||||
void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keeper)
|
||||
{
|
||||
PrimeTower primetower();
|
||||
PrimeTower primetower;
|
||||
|
||||
gcode.preSetup(storage.meshgroup);
|
||||
|
||||
@@ -21,13 +21,25 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
gcode.resetTotalPrintTimeAndFilament();
|
||||
}
|
||||
|
||||
if (command_socket)
|
||||
command_socket->beginGCode();
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->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);
|
||||
@@ -59,21 +71,24 @@ 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);
|
||||
|
||||
//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)
|
||||
{
|
||||
command_socket->sendGCodeLayer();
|
||||
command_socket->endSendSlicedObject();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -83,134 +98,119 @@ void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
|
||||
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extr);
|
||||
CoastingConfig& coasting_config = storage.coasting_config.back();
|
||||
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") / 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") / 100.0;
|
||||
coasting_config.coasting_volume = train->getSettingInCubicMillimeters("coasting_volume");
|
||||
coasting_config.coasting_min_volume = train->getSettingInCubicMillimeters("coasting_min_volume");
|
||||
coasting_config.coasting_speed = train->getSettingInPercentage("coasting_speed") / 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_extruder_nr"));
|
||||
storage.support_config.init(getSettingInMillimetersPerSecond("speed_support_lines"), 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"));
|
||||
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
storage.primeTower.initConfigs(storage.meshgroup, storage.retraction_config_per_extruder);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
|
||||
{
|
||||
if (!command_socket)
|
||||
if (!CommandSocket::isInstantiated())
|
||||
{
|
||||
std::ostringstream prefix;
|
||||
prefix << "FLAVOR:" << toString(gcode.getFlavor());
|
||||
gcode.writeComment(prefix.str().c_str());
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
gcode.writeComment("TIME:666");
|
||||
gcode.writeComment("MATERIAL:666");
|
||||
gcode.writeComment("MATERIAL2:-1");
|
||||
}
|
||||
std::string prefix = gcode.getFileHeader();
|
||||
gcode.writeCode(prefix.c_str());
|
||||
}
|
||||
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,7 +231,8 @@ void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
|
||||
gcode.resetExtrusionValue();
|
||||
gcode.setZ(max_object_height + 5000);
|
||||
gcode.writeMove(gcode.getPositionXY(), getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
gcode.writeMove(Point(storage.model_min.x, storage.model_min.y), getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
last_position_planned = Point(storage.model_min.x, storage.model_min.y);
|
||||
gcode.writeMove(last_position_planned, getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
|
||||
@@ -243,125 +244,126 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
|
||||
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"));
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
// some infill config for all lines infill generation below
|
||||
Polygons* in_between = nullptr;
|
||||
int offset_from_poly_outline = 0;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
double fill_overlap = 0; // raft line shouldn't be expanded - there is no boundary polygon printed
|
||||
Polygons raft_polygons; // should remain empty, since we only have the lines pattern for the raft...
|
||||
|
||||
{ // raft base layer
|
||||
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"));
|
||||
|
||||
int layer_nr = -n_raft_surface_layers - 2;
|
||||
gcode.writeLayerComment(layer_nr);
|
||||
gcode.writeComment("RAFT");
|
||||
int64_t z = getSettingInMicrons("raft_base_thickness");
|
||||
gcode.setZ(z);
|
||||
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, layer_nr, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setCombing(false);
|
||||
int layer_height = getSettingInMicrons("raft_base_thickness");
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_base_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, 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);
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, raft_base_config.getLayerHeight());
|
||||
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &raft_base_config);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->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);
|
||||
sendPolygons(SupportType, layer_nr, raftLines, raft_base_config.getLineWidth());
|
||||
double fill_angle = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), fill_overlap, fill_angle);
|
||||
infill_comp.generate(raft_polygons, raftLines, in_between);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config, SpaceFillType::Lines);
|
||||
|
||||
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
|
||||
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_interface_thickness"));
|
||||
raft_interface_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
|
||||
int layer_nr = -n_raft_surface_layers - 1;
|
||||
gcode.writeLayerComment(layer_nr);
|
||||
gcode.writeComment("RAFT");
|
||||
int64_t z = train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness");
|
||||
gcode.setZ(z);
|
||||
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, layer_nr, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setCombing(false);
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, raft_interface_config.getLayerHeight());
|
||||
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_interface_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->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);
|
||||
sendPolygons(SupportType, layer_nr, raftLines, raft_interface_config.getLineWidth());
|
||||
double fill_angle = train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90;
|
||||
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), fill_overlap, fill_angle);
|
||||
infill_comp.generate(raft_polygons, raftLines, in_between);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config, SpaceFillType::Lines);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
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_surface_thickness"));
|
||||
raft_surface_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
int layer_height = train->getSettingInMicrons("raft_surface_thickness");
|
||||
|
||||
for (int raftSurfaceLayer=1; raftSurfaceLayer <= n_raft_surface_layers; raftSurfaceLayer++)
|
||||
{ // raft surface layers
|
||||
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
|
||||
gcode.writeLayerComment(-1);
|
||||
gcode.writeComment("RAFT");
|
||||
int64_t z = train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness") + train->getSettingInMicrons("raft_surface_thickness")*raftSurfaceLayer;
|
||||
gcode.setZ(z);
|
||||
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, layer_nr, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setCombing(false);
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, raft_surface_config.getLayerHeight());
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_surface_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->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);
|
||||
sendPolygons(SupportType, layer_nr, raft_lines, raft_surface_config.getLineWidth());
|
||||
double fill_angle = 90 * raftSurfaceLayer;
|
||||
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), fill_overlap, fill_angle);
|
||||
infill_comp.generate(raft_polygons, raft_lines, in_between);
|
||||
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config, SpaceFillType::Lines);
|
||||
|
||||
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);
|
||||
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers);
|
||||
|
||||
int layer_thickness = getSettingInMicrons("layer_height");
|
||||
if (layer_nr == 0 && !has_raft)
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
}
|
||||
|
||||
ExtruderTrain* current_extruder_train = storage.meshgroup->getExtruderTrain(current_extruder_planned);
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
setConfigInsets(mesh, layer_thickness);
|
||||
setConfigSkin(mesh, layer_thickness);
|
||||
setConfigInfill(mesh, layer_thickness);
|
||||
}
|
||||
|
||||
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.
|
||||
int64_t comb_offset_from_outlines = current_extruder_train->getSettingInMicrons((current_extruder_train->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 2; // TODO: only used when there is no second wall.
|
||||
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
|
||||
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config, getSettingInMillimetersPerSecond("speed_travel"), getSettingBoolean("retraction_combing"), layer_nr, comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode.setZ(z);
|
||||
gcode.resetStartPosition();
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(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)
|
||||
{
|
||||
@@ -379,22 +381,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);
|
||||
|
||||
@@ -403,41 +405,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]);
|
||||
|
||||
@@ -447,7 +428,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
|
||||
}
|
||||
}
|
||||
@@ -458,21 +439,20 @@ void FffGcodeWriter::processDraftShield(SliceDataStorage& storage, GCodePlanner&
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int draft_shield_height = getSettingInMicrons("draft_shield_height");
|
||||
int layer_height_0 = getSettingInMicrons("layer_height_0");
|
||||
int layer_height = getSettingInMicrons("layer_height");
|
||||
|
||||
|
||||
int max_screen_layer = (draft_shield_height - layer_height_0) / layer_height + 1;
|
||||
|
||||
|
||||
if (int(layer_nr) > max_screen_layer)
|
||||
{
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(SliceDataStorage& storage, int current_extruder)
|
||||
@@ -520,11 +500,10 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& stora
|
||||
{
|
||||
polygons.add(layer->parts[partNr].outline);
|
||||
}
|
||||
if (mesh->getSettingBoolean("magic_spiralize"))
|
||||
mesh->inset0_config.spiralize = true;
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config);
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config, nullptr, z_seam_type, mesh->getSettingBoolean("magic_spiralize"));
|
||||
|
||||
addMeshOpenPolyLinesToGCode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
|
||||
@@ -543,7 +522,7 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, Slic
|
||||
lines.add(p);
|
||||
}
|
||||
}
|
||||
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config);
|
||||
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config, SpaceFillType::PolyLines);
|
||||
|
||||
}
|
||||
|
||||
@@ -565,7 +544,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]);
|
||||
@@ -578,9 +557,12 @@ 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 infill_line_width = mesh->infill_config[0].getLineWidth();
|
||||
|
||||
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
|
||||
@@ -600,15 +582,23 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
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, mesh->skin_config.getLineWidth());
|
||||
int64_t skin_overlap = infill_overlap;
|
||||
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"))
|
||||
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons("machine_nozzle_size") * 1);
|
||||
{
|
||||
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons((mesh->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
|
||||
}
|
||||
}
|
||||
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
@@ -627,13 +617,13 @@ void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMes
|
||||
//Print the thicker infill lines first. (double or more layer thickness, infill combined with previous layers)
|
||||
for(unsigned int n=1; n<part.infill_area.size(); n++)
|
||||
{
|
||||
Infill infill_comp(mesh->getSettingAsFillMethod("infill_pattern"), part.infill_area[n], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
|
||||
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
|
||||
Infill infill_comp(infill_pattern, part.infill_area[n], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
|
||||
Polygons infill_polygons;
|
||||
Polygons infill_lines;
|
||||
infill_comp.generate(infill_polygons, infill_lines, nullptr);
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[n]);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n]);
|
||||
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n], (infill_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -656,13 +646,12 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMe
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[0]);
|
||||
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0]);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type)
|
||||
@@ -670,12 +659,17 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
|
||||
bool compensate_overlap = mesh->getSettingBoolean("travel_compensate_overlapping_walls_enabled");
|
||||
if (mesh->getSettingAsCount("wall_line_count") > 0)
|
||||
{
|
||||
bool spiralize = false;
|
||||
if (mesh->getSettingBoolean("magic_spiralize"))
|
||||
{
|
||||
if (static_cast<int>(layer_nr) >= mesh->getSettingAsCount("bottom_layers"))
|
||||
mesh->inset0_config.spiralize = true;
|
||||
{
|
||||
spiralize = true;
|
||||
}
|
||||
if (static_cast<int>(layer_nr) == mesh->getSettingAsCount("bottom_layers") && part.insets.size() > 0)
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config);
|
||||
{ // on the last normal layer first make the outer wall normally and then start a second outer wall from the same hight, but gradually moving upward
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config, nullptr, EZSeamType::SHORTEST, false);
|
||||
}
|
||||
}
|
||||
for(int inset_number=part.insets.size()-1; inset_number>-1; inset_number--)
|
||||
{
|
||||
@@ -683,13 +677,13 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
|
||||
{
|
||||
if (!compensate_overlap)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->inset0_config, nullptr, z_seam_type);
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->inset0_config, nullptr, z_seam_type, spiralize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Polygons& outer_wall = part.insets[0];
|
||||
WallOverlapComputation wall_overlap_computation(outer_wall, mesh->getSettingInMicrons("wall_line_width_0"));
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->inset0_config, &wall_overlap_computation, z_seam_type);
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->inset0_config, &wall_overlap_computation, z_seam_type, spiralize);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -718,15 +712,10 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
}
|
||||
Polygons* inner_skin_outline = nullptr;
|
||||
int offset_from_inner_skin_outline = 0;
|
||||
if (pattern == EFillMethod::CONCENTRIC)
|
||||
{
|
||||
offset_from_inner_skin_outline = -extrusion_width/2;
|
||||
}
|
||||
else
|
||||
if (pattern != EFillMethod::CONCENTRIC)
|
||||
{
|
||||
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)
|
||||
@@ -735,7 +724,13 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
offset_from_inner_skin_outline = -extrusion_width/2;
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
generateLineInfill(skin_part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
|
||||
Polygons result_polygons; // should remain empty, since we're only allowing for lines infill
|
||||
Polygons* in_between = nullptr;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
int line_distance = extrusion_width;
|
||||
int outline_offset = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, skin_part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
|
||||
infill_comp.generate(result_polygons, skin_lines, in_between);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -749,17 +744,30 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
infill_comp.generate(skin_polygons, skin_lines, &part.perimeterGaps);
|
||||
|
||||
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());
|
||||
|
||||
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
// handle gaps between perimeters etc.
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
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);
|
||||
Polygons result_polygons; // should remain empty, since we're only allowing for lines infill
|
||||
Polygons* in_between = nullptr;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
int line_distance = extrusion_width;
|
||||
int outline_offset = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
|
||||
infill_comp.generate(result_polygons, perimeter_gap_lines, in_between);
|
||||
|
||||
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,7 +786,7 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
|
||||
if (print_support_before_rest != before_rest)
|
||||
return;
|
||||
|
||||
gcode_layer.setCombing(false);
|
||||
gcode_layer.setIsInside(false);
|
||||
|
||||
int current_extruder_nr = gcode_layer.getExtruder();
|
||||
|
||||
@@ -809,7 +817,7 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int support_line_distance = getSettingInMicrons("support_line_distance");
|
||||
int extrusion_width = storage.support_config.getLineWidth();
|
||||
EFillMethod support_pattern = getSettingAsFillMethod("support_pattern");
|
||||
@@ -817,7 +825,6 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
|
||||
|
||||
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
|
||||
|
||||
double infill_overlap = storage.meshgroup->getExtruderTrain(support_extruder_nr)->getSettingInPercentage("infill_overlap");
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_extruder_nr);
|
||||
|
||||
@@ -825,7 +832,7 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
|
||||
|
||||
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]);
|
||||
@@ -836,21 +843,25 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
|
||||
{
|
||||
PolygonsPart& island = support_islands[island_order_optimizer.polyOrder[n]];
|
||||
|
||||
double infill_overlap = 0; // support infill should not be expanded outward
|
||||
|
||||
int offset_from_outline = 0;
|
||||
Infill infill_comp(support_pattern, island, offset_from_outline, false, extrusion_width, support_line_distance, infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
|
||||
bool remove_overlapping_perimeters = false;
|
||||
if (support_pattern == EFillMethod::GRID || support_pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
Polygons boundary;
|
||||
PolygonUtils::offsetSafe(island, -extrusion_width / 2, extrusion_width, boundary, remove_overlapping_perimeters);
|
||||
gcode_layer.addPolygonsByOptimizer(boundary, &storage.support_config);
|
||||
offset_from_outline = -extrusion_width;
|
||||
infill_overlap = storage.meshgroup->getExtruderTrain(support_extruder_nr)->getSettingInPercentage("infill_overlap"); // support lines area should be expanded outward to overlap with the boundary polygon
|
||||
}
|
||||
Infill infill_comp(support_pattern, island, offset_from_outline, remove_overlapping_perimeters, extrusion_width, support_line_distance, infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
|
||||
Polygons support_polygons;
|
||||
Polygons support_lines;
|
||||
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());
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config, (support_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,7 +893,7 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
|
||||
{
|
||||
fillAngle = 45 + (layer_nr % 2) * 90; // alternate between the two kinds of diagonal: / and \ .
|
||||
}
|
||||
double infill_overlap = 0;
|
||||
double infill_overlap = 0; // the roofs should never be expanded outwards
|
||||
int outline_offset = 0;
|
||||
|
||||
Infill infill_comp(pattern, storage.support.supportLayers[layer_nr].roofs, outline_offset, false, storage.support_roof_config.getLineWidth(), support_line_distance, infill_overlap, fillAngle, false, true);
|
||||
@@ -891,9 +902,7 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
|
||||
infill_comp.generate(support_polygons, support_lines, nullptr);
|
||||
|
||||
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());
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
|
||||
@@ -933,53 +942,15 @@ void FffGcodeWriter::addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcod
|
||||
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);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::finalize()
|
||||
{
|
||||
if (command_socket)
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
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());
|
||||
std::string prefix = gcode.getFileHeader(gcode.getTotalPrintTime(), gcode.getTotalFilamentUsed(0), gcode.getTotalFilamentUsed(1));
|
||||
CommandSocket::getInstance()->sendGCodePrefix(prefix);
|
||||
}
|
||||
|
||||
gcode.finalize(max_object_height, 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);
|
||||
|
||||
|
||||
+26
-39
@@ -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,13 @@
|
||||
#include "gcodePlanner.h"
|
||||
#include "gcodeExport.h"
|
||||
#include "commandSocket.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "PrimeTower.h"
|
||||
#include "FanSpeedLayerTime.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
|
||||
#include "LayerPlanBuffer.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -26,14 +33,16 @@ 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,30 +51,26 @@ 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, gcode)
|
||||
, last_position_planned(no_point)
|
||||
, current_extruder_planned(0) // TODO: make configurable
|
||||
{
|
||||
meshgroup_number = 1;
|
||||
max_object_height = 0;
|
||||
command_socket = NULL;
|
||||
}
|
||||
void resetFileNumber()
|
||||
{
|
||||
meshgroup_number = 1;
|
||||
}
|
||||
|
||||
void setCommandSocket(CommandSocket* socket)
|
||||
{
|
||||
command_socket = socket;
|
||||
}
|
||||
|
||||
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
{
|
||||
if (command_socket)
|
||||
command_socket->sendPolygons(type, layer_nr, polygons, line_width);
|
||||
}
|
||||
|
||||
bool setTargetFile(const char* filename)
|
||||
{
|
||||
output_file.open(filename);
|
||||
@@ -95,20 +100,17 @@ 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);
|
||||
|
||||
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);
|
||||
/*!
|
||||
* initialize GcodePathConfig config parameters which don't change over all layers
|
||||
*/
|
||||
void initConfigs(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Set temperatures and perform initial priming.
|
||||
@@ -138,13 +140,6 @@ private:
|
||||
*/
|
||||
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, 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);
|
||||
|
||||
/*!
|
||||
* Add the skirt to the gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
@@ -301,14 +296,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.
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "raft.h"
|
||||
#include "debug.h"
|
||||
#include "Progress.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -23,9 +24,6 @@ namespace cura
|
||||
|
||||
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
|
||||
{
|
||||
if (commandSocket)
|
||||
commandSocket->beginSendSlicedObject();
|
||||
|
||||
if (!sliceModel(meshgroup, timeKeeper, storage))
|
||||
{
|
||||
return false;
|
||||
@@ -38,7 +36,7 @@ bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* me
|
||||
|
||||
bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeeper, SliceDataStorage& storage) /// slices the model
|
||||
{
|
||||
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper);
|
||||
|
||||
storage.model_min = meshgroup->min();
|
||||
storage.model_max = meshgroup->max();
|
||||
@@ -65,7 +63,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
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.
|
||||
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper); //Continue directly with the inset stage, which will also immediately stop.
|
||||
return true; //This is NOT an error state!
|
||||
}
|
||||
|
||||
@@ -79,18 +77,18 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
for(SlicerLayer& layer : slicer->layers)
|
||||
{
|
||||
//Reporting the outline here slows down the engine quite a bit, so only do so when debugging.
|
||||
//sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
|
||||
//sendPolygons("openoutline", layer_nr, layer.openPolygonList);
|
||||
sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
|
||||
sendPolygons("openoutline", layer_nr, layer.openPolygonList);
|
||||
}
|
||||
*/
|
||||
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size(), commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
|
||||
}
|
||||
|
||||
log("Layer count: %i\n", layer_count);
|
||||
|
||||
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);
|
||||
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
|
||||
//carveMultipleVolumes(storage.meshes);
|
||||
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
|
||||
|
||||
@@ -116,8 +114,13 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
layer.printZ +=
|
||||
meshStorage.getSettingInMicrons("raft_base_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_interface_thickness")
|
||||
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("raft_surface_thickness") //raft_surface_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_airgap");
|
||||
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("raft_surface_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_airgap")
|
||||
- meshStorage.getSettingInMicrons("layer_0_z_overlap"); // shift all layers (except 0) down
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
layer.printZ += meshStorage.getSettingInMicrons("layer_0_z_overlap"); // undo shifting down of first layer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,16 +129,16 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
meshStorage.layer_nr_max_filled_layer = layer_nr; // last set by the highest non-empty layer
|
||||
}
|
||||
|
||||
if (commandSocket)
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0 && !has_raft? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
|
||||
}
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size(), commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -152,7 +155,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
{
|
||||
processInsets(storage, layer_number);
|
||||
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers, commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers);
|
||||
}
|
||||
|
||||
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers);
|
||||
@@ -163,21 +166,24 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
return;
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
|
||||
|
||||
AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
|
||||
AreaSupport::generateSupportAreas(storage, total_layers);
|
||||
/*
|
||||
if (storage.support.generated)
|
||||
{
|
||||
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
|
||||
{
|
||||
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
|
||||
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Infill, layer_idx, support, 100); //getSettingInMicrons("support_line_width"));
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
|
||||
int mesh_max_bottom_layer_count = 0;
|
||||
if (getSettingBoolean("magic_spiralize"))
|
||||
{
|
||||
@@ -190,14 +196,14 @@ 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);
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned int combined_infill_layers = mesh.getSettingInMicrons("infill_sparse_thickness") / std::max(mesh.getSettingInMicrons("layer_height"), 1); //How many infill layers to combine to obtain the requested sparse thickness.
|
||||
combineInfillLayers(mesh,combined_infill_layers);
|
||||
}
|
||||
|
||||
@@ -217,17 +223,6 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
{
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,16 +241,6 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
|
||||
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
|
||||
inset_count += layer_nr % 2;
|
||||
generateInsets(layer, mesh.getSettingInMicrons("machine_nozzle_size"), line_width_0, line_width_x, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
{
|
||||
if (layer->parts[partNr].insets.size() > 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
@@ -268,7 +253,6 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
|
||||
segment.add(polyline[point_idx-1]);
|
||||
segment.add(polyline[point_idx]);
|
||||
}
|
||||
sendPolygons(Inset0Type, layer_nr, segments, mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,24 +299,25 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int
|
||||
}
|
||||
}
|
||||
|
||||
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 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, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), 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"));
|
||||
int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
|
||||
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)
|
||||
bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10;
|
||||
if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC)
|
||||
{
|
||||
infill_skin_overlap = skin_extrusion_width / 2;
|
||||
}
|
||||
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap);
|
||||
generateInfill(layer_nr, mesh, innermost_wall_extrusion_width, infill_skin_overlap, wall_line_count);
|
||||
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
|
||||
{
|
||||
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
|
||||
@@ -342,20 +327,6 @@ void FffPolygonGenerator::processSkins(SliceDataStorage& storage, unsigned int l
|
||||
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool frontend_can_show_polygon_as_filled_polygon = false;
|
||||
if (frontend_can_show_polygon_as_filled_polygon)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
for(SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,7 +406,6 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
|
||||
Polygons skirt_sent = storage.skirt[0];
|
||||
for (int extruder = 1; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
skirt_sent.add(storage.skirt[extruder]);
|
||||
sendPolygons(SkirtType, 0, skirt_sent, getSettingInMicrons("skirt_line_width"));
|
||||
}
|
||||
|
||||
|
||||
@@ -471,7 +441,7 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
|
||||
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
|
||||
{
|
||||
int r = rand() % (fuzziness * 2) - fuzziness;
|
||||
Point perp_to_p0p1 = crossZ(p0p1);
|
||||
Point perp_to_p0p1 = turn90CCW(p0p1);
|
||||
Point fuzz = normal(perp_to_p0p1, r);
|
||||
Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz;
|
||||
result.add(pa);
|
||||
@@ -496,7 +466,6 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
|
||||
}
|
||||
}
|
||||
skin = results;
|
||||
sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/gettime.h"
|
||||
#include "settings.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "commandSocket.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -21,28 +23,16 @@ namespace cura
|
||||
*
|
||||
* The main function of this class is FffPolygonGenerator::generateAreas().
|
||||
*/
|
||||
class FffPolygonGenerator : public SettingsMessenger
|
||||
class FffPolygonGenerator : public SettingsMessenger, NoCopy
|
||||
{
|
||||
private:
|
||||
CommandSocket* commandSocket;
|
||||
public:
|
||||
/*!
|
||||
* Basic constructor; doesn't set the FffAreaGenerator::commandSocket .
|
||||
* Basic constructor
|
||||
*/
|
||||
FffPolygonGenerator(SettingsBase* settings_)
|
||||
: SettingsMessenger(settings_)
|
||||
, commandSocket(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the FffAreaGenerator::commandSocket
|
||||
*/
|
||||
void setCommandSocket(CommandSocket* socket)
|
||||
{
|
||||
commandSocket = socket;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Slice the \p object, process the outline information into inset perimeter polygons, support area polygons, etc.
|
||||
@@ -54,17 +44,6 @@ public:
|
||||
bool generateAreas(SliceDataStorage& storage, MeshGroup* object, TimeKeeper& timeKeeper);
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Send polygons over the command socket, if there is one.
|
||||
* \param type The type of polygon to send
|
||||
* \param layer_nr The layer number at which the polygons occur
|
||||
* \param polygons The polygons to be sent
|
||||
*/
|
||||
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
{
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(type, layer_nr, polygons, line_width);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Slice the \p object and store the outlines in the \p storage.
|
||||
@@ -112,7 +91,7 @@ private:
|
||||
* \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.
|
||||
|
||||
+19
-5
@@ -11,6 +11,7 @@ std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_
|
||||
std::stringstream sstream;
|
||||
if (first_meshgroup)
|
||||
{
|
||||
sstream << getAllLocalSettingsString(); // global settings
|
||||
sstream << " -g";
|
||||
}
|
||||
else
|
||||
@@ -64,16 +65,25 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
|
||||
TimeKeeper time_keeper_total;
|
||||
|
||||
if (meshgroup->meshes.empty())
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
|
||||
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");
|
||||
|
||||
Weaver w(this);
|
||||
w.weave(meshgroup, command_socket);
|
||||
w.weave(meshgroup);
|
||||
|
||||
log("starting Neith Gcode generation...\n");
|
||||
Wireframe2gcode gcoder(w, gcode_writer.gcode, this);
|
||||
gcoder.writeGCode(command_socket);
|
||||
gcoder.writeGCode();
|
||||
log("finished Neith Gcode generation...\n");
|
||||
|
||||
} else
|
||||
@@ -84,13 +94,17 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
gcode_writer.setCommandSocket(command_socket);
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper, command_socket);
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper);
|
||||
gcode_writer.writeGCode(storage, time_keeper);
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
CommandSocket::getInstance()->sendLayerData();
|
||||
}
|
||||
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
|
||||
|
||||
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
|
||||
|
||||
@@ -26,7 +26,6 @@ private:
|
||||
, gcode_writer(this)
|
||||
, first_meshgroup(true)
|
||||
{
|
||||
command_socket = NULL;
|
||||
}
|
||||
public:
|
||||
static FffProcessor* getInstance()
|
||||
@@ -37,7 +36,6 @@ public:
|
||||
private:
|
||||
FffPolygonGenerator polygon_generator;
|
||||
FffGcodeWriter gcode_writer;
|
||||
CommandSocket* command_socket; // TODO: replace all refs to command_socket by CommandSocket::getInstance()
|
||||
|
||||
bool first_meshgroup;
|
||||
|
||||
@@ -55,13 +53,6 @@ public:
|
||||
gcode_writer.resetFileNumber();
|
||||
}
|
||||
|
||||
void setCommandSocket(CommandSocket* socket)
|
||||
{
|
||||
command_socket = socket;
|
||||
gcode_writer.setCommandSocket(socket);
|
||||
polygon_generator.setCommandSocket(socket);
|
||||
}
|
||||
|
||||
bool setTargetFile(const char* filename)
|
||||
{
|
||||
return gcode_writer.setTargetFile(filename);
|
||||
|
||||
@@ -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,223 @@
|
||||
/** 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
|
||||
}
|
||||
while (!buffer.empty())
|
||||
{
|
||||
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
buffer.pop_front();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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,131 @@
|
||||
#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
|
||||
{
|
||||
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, GCodeExport& gcode)
|
||||
: SettingsMessenger(settings)
|
||||
, 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 (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
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
|
||||
+119
-43
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <algorithm> // min
|
||||
|
||||
#include "utils/linearAlg2D.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
@@ -12,6 +14,7 @@ void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath&
|
||||
double speed_mod = old_line_width / new_line_width_mm;
|
||||
double extrusion_mod = new_line_width_mm / old_line_width;
|
||||
double new_speed = std::min(speed * speed_mod, 150.0); // TODO: hardcoded value: max extrusion speed is 150 mm/s = 9000 mm/min
|
||||
sendPolygon(last_path.config->type, gcode.getPositionXY(), to, last_path.getLineWidth());
|
||||
gcode.writeMove(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod);
|
||||
}
|
||||
|
||||
@@ -20,10 +23,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,75 +40,150 @@ 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;
|
||||
};
|
||||
|
||||
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first)
|
||||
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
|
||||
{
|
||||
int64_t max_line_width = nozzle_size * 3 / 2;
|
||||
|
||||
|
||||
unsigned int idx = path_idx_first_move;
|
||||
if (idx + 3 > paths.size()-1) return false;
|
||||
if (paths[idx+0].config != &travelConfig) return false;
|
||||
if (paths[idx+1].points.size() > 1) return false;
|
||||
if (paths[idx+1].config == &travelConfig) return false;
|
||||
// if (paths[idx+2].points.size() > 1) return false;
|
||||
if (paths[idx+2].config != &travelConfig) return false;
|
||||
if (paths[idx+3].points.size() > 1) return false;
|
||||
if (paths[idx+3].config == &travelConfig) return false;
|
||||
if (idx + 3 > paths.size()-1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ( paths[idx+0].config != &travelConfig // must be travel
|
||||
|| paths[idx+1].points.size() > 1 // extrusion path is single line
|
||||
|| paths[idx+1].config == &travelConfig // must be extrusion
|
||||
// || paths[idx+2].points.size() > 1 // travel must be direct
|
||||
|| paths[idx+2].config != &travelConfig // must be travel
|
||||
|| paths[idx+3].points.size() > 1 // extrusion path is single line
|
||||
|| paths[idx+3].config == &travelConfig // must be extrusion
|
||||
|| paths[idx+1].config != paths[idx+3].config // both extrusion moves should have the same config
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(paths[idx+1].config->type == PrintFeatureType::Infill || paths[idx+1].config->type == PrintFeatureType::Skin))
|
||||
{ // only (skin) infill lines can be merged (note that the second extrusion line config is already checked to be the same as the first in code above)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (paths[idx+1].space_fill_type != SpaceFillType::Lines || paths[idx+3].space_fill_type != SpaceFillType::Lines)
|
||||
{ // both extrusion moves must be of lines space filling type!
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t line_width = paths[idx+1].config->getLineWidth();
|
||||
|
||||
Point& a = paths[idx+0].points.back(); // first extruded line from
|
||||
Point& b = paths[idx+1].points.back(); // first extruded line to
|
||||
Point& c = paths[idx+2].points.back(); // second extruded line from
|
||||
Point& d = paths[idx+3].points.back(); // second extruded line to
|
||||
|
||||
return isConvertible(a, b, c, d, line_width, first_middle, second_middle, resulting_line_width, use_second_middle_as_first);
|
||||
}
|
||||
|
||||
bool MergeInfillLines::isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
|
||||
{
|
||||
use_second_middle_as_first = false;
|
||||
int64_t max_line_width = nozzle_size * 3 / 2;
|
||||
|
||||
Point ab = b - a;
|
||||
Point cd = d - c;
|
||||
|
||||
|
||||
if (b == c)
|
||||
{
|
||||
return false; // the line segments are connected!
|
||||
}
|
||||
|
||||
int64_t ab_size = vSize(ab);
|
||||
int64_t cd_size = vSize(cd);
|
||||
|
||||
if (ab_size > nozzle_size * 5 || cd_size > nozzle_size * 5)
|
||||
{
|
||||
return false; // infill lines are too long; otherwise infill lines might be merged when the next infill line is coincidentally shorter like |, would become \ ...
|
||||
}
|
||||
|
||||
// if the lines are in the same direction then abs( dot(ab,cd) / |ab| / |cd| ) == 1
|
||||
int64_t prod = dot(ab,cd);
|
||||
if (std::abs(prod) + 400 < vSize(ab) * vSize(cd)) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
|
||||
if (std::abs(prod) + 400 < ab_size * cd_size) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
|
||||
{
|
||||
return false; // extrusion moves not in the same or opposite diraction
|
||||
if (prod < 0) { ab = ab * -1; }
|
||||
}
|
||||
|
||||
|
||||
Point infill_vector = (cd + ab) / 2;
|
||||
|
||||
if (!shorterThen(infill_vector, 5 * nozzle_size)) return false; // infill lines too far apart
|
||||
|
||||
// make lines in the same direction by flipping one
|
||||
if (prod < 0)
|
||||
{
|
||||
ab = ab * -1;
|
||||
}
|
||||
else if (prod == 0)
|
||||
{
|
||||
return false; // lines are orthogonal!
|
||||
}
|
||||
else if (b == d || a == c)
|
||||
{
|
||||
return false; // the line segments are connected!
|
||||
}
|
||||
|
||||
first_middle = (use_second_middle_as_first)?
|
||||
second_middle :
|
||||
(a + b) / 2;
|
||||
second_middle = (c + d) / 2;
|
||||
|
||||
Point dir_vector_perp = crossZ(second_middle - first_middle);
|
||||
Point dir_vector_perp = turn90CCW(second_middle - first_middle);
|
||||
int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length
|
||||
if (dir_vector_perp_length == 0) return false;
|
||||
if (dir_vector_perp_length > 5 * nozzle_size) return false; // infill lines too far apart
|
||||
|
||||
|
||||
line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
|
||||
if (line_width > max_line_width) return false; // combined lines would be too wide
|
||||
if (line_width == 0) return false; // dot is zero, so lines are in each others extension, not next to eachother
|
||||
|
||||
{ // check whether the two lines are adjacent
|
||||
Point ca = first_middle - c;
|
||||
double ca_size = vSizeMM(ca);
|
||||
double cd_size = vSizeMM(cd);
|
||||
double prod = INT2MM(dot(ca, cd));
|
||||
double fraction = prod / ( ca_size * cd_size );
|
||||
int64_t line2line_dist = MM2INT(cd_size * std::sqrt(1.0 - fraction * fraction));
|
||||
|
||||
if (line2line_dist + 20 > paths[idx+1].config->getLineWidth()) return false; // there is a gap between the two lines
|
||||
if (dir_vector_perp_length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (dir_vector_perp_length > 5 * nozzle_size)
|
||||
{
|
||||
return false; // infill lines too far apart
|
||||
}
|
||||
|
||||
Point infill_vector = (cd + ab) / 2; // (similar to) average line / direction of the infill
|
||||
|
||||
// compute the resulting line width
|
||||
resulting_line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
|
||||
if (resulting_line_width > max_line_width)
|
||||
{
|
||||
return false; // combined lines would be too wide
|
||||
}
|
||||
if (resulting_line_width == 0)
|
||||
{
|
||||
return false; // dot is zero, so lines are in each others extension, not next to eachother
|
||||
}
|
||||
|
||||
// check whether two lines are adjacent (note: not 'line segments' but 'lines')
|
||||
Point ac = c - first_middle;
|
||||
Point infill_vector_perp = turn90CCW(infill_vector);
|
||||
int64_t perp_proj = dot(ac, infill_vector_perp);
|
||||
int64_t infill_vector_perp_length = vSize(infill_vector_perp);
|
||||
if (std::abs(std::abs(perp_proj) / infill_vector_perp_length - line_width) > 20) // it should be the case that dot(ac, infill_vector_perp) / |infill_vector_perp| == line_width
|
||||
{
|
||||
return false; // lines are too far apart or too close together
|
||||
}
|
||||
|
||||
// check whether the two line segments are adjacent.
|
||||
// full infill in a narrow area might result in line segments with arbitrary distance between them
|
||||
// the more the narrow passage in the area gets aligned with the infill direction, the further apart the line segments will be
|
||||
// however, distant line segments might also be due to different narrow passages, so we limit the distance between merged line segments.
|
||||
if (!LinearAlg2D::lineSegmentsAreCloserThan(a, b, c, d, line_width * 2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,22 +12,42 @@ class MergeInfillLines
|
||||
{
|
||||
// void merge(Point& from, Point& p0, Point& p1);
|
||||
GCodeExport& gcode; //!< Where to write the combined line to
|
||||
int layer_nr; //!< The current layer number
|
||||
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
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
* Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
|
||||
* \param path_idx_first_move Index into MergeInfillLines::paths to the travel before the two extrusion moves udner consideration
|
||||
* \param first_middle Output parameter: the middle of the first extrusion move
|
||||
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
|
||||
* \param line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
|
||||
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
|
||||
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
|
||||
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
|
||||
*/
|
||||
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first);
|
||||
|
||||
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
|
||||
|
||||
/*!
|
||||
* Whether the two consecutive extrusion paths (ab and cd) are convitrible to a single line segment.
|
||||
*
|
||||
* Note: as an optimization the \p second_middle from the previous call to isConvertible can be used for \p first_middle, instead of recomputing it.
|
||||
*
|
||||
* \param a first from
|
||||
* \param b first to
|
||||
* \param c second from
|
||||
* \param d second to
|
||||
* \param line_width The line width of the moves
|
||||
* \param first_middle Output parameter: the middle of the first extrusion move
|
||||
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
|
||||
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
|
||||
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
|
||||
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
|
||||
*/
|
||||
bool isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
|
||||
|
||||
/*!
|
||||
* Write an extrusion move with compensated width and compensated speed so that the material flow will be the same.
|
||||
*
|
||||
@@ -41,8 +61,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, int layer_nr, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size)
|
||||
: gcode(gcode), layer_nr(layer_nr), 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.
|
||||
@@ -58,6 +78,21 @@ public:
|
||||
*/
|
||||
bool mergeInfillLines(double speed, unsigned int& path_idx);
|
||||
|
||||
/*!
|
||||
* send a polygon through the command socket from the previous point to the given point
|
||||
*/
|
||||
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
|
||||
{
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
// we should send this travel as a non-retraction move
|
||||
cura::Polygons pathPoly;
|
||||
PolygonRef path = pathPoly.newPoly();
|
||||
path.add(from);
|
||||
path.add(to);
|
||||
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+44
-14
@@ -64,26 +64,38 @@ bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rb");
|
||||
|
||||
fseek(f, 0L, SEEK_END);
|
||||
long long file_size = ftell(f); //The file size is the position of the cursor after seeking to the end.
|
||||
rewind(f); //Seek back to start.
|
||||
size_t face_count = (file_size - 80 - sizeof(uint32_t)) / 50; //Subtract the size of the header. Every face uses exactly 50 bytes.
|
||||
|
||||
char buffer[80];
|
||||
uint32_t faceCount;
|
||||
//Skip the header
|
||||
if (fread(buffer, 80, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
//Read the face count
|
||||
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
|
||||
|
||||
uint32_t reported_face_count;
|
||||
//Read the face count. We'll use it as a sort of redundancy code to check for file corruption.
|
||||
if (fread(&reported_face_count, sizeof(uint32_t), 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
if (reported_face_count != face_count)
|
||||
{
|
||||
logWarning("Face count reported by file (%s) is not equal to actual face count (%s). File could be corrupt!\n", std::to_string(reported_face_count).c_str(), std::to_string(face_count).c_str());
|
||||
}
|
||||
|
||||
//For each face read:
|
||||
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
|
||||
// Every Face is 50 Bytes: Normal(3*float), Vertices(9*float), 2 Bytes Spacer
|
||||
mesh->faces.reserve(faceCount);
|
||||
mesh->vertices.reserve(faceCount);
|
||||
for(unsigned int i=0;i<faceCount;i++)
|
||||
mesh->faces.reserve(face_count);
|
||||
mesh->vertices.reserve(face_count);
|
||||
for (unsigned int i = 0; i < face_count; i++)
|
||||
{
|
||||
if (fread(buffer, 50, 1, f) != 1)
|
||||
{
|
||||
@@ -105,10 +117,31 @@ bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "r");
|
||||
char buffer[6];
|
||||
if (f == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Skip any whitespace at the beginning of the file.
|
||||
unsigned long long num_whitespace = 0; //Number of whitespace characters.
|
||||
unsigned char whitespace;
|
||||
if (fread(&whitespace, 1, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
while(isspace(whitespace))
|
||||
{
|
||||
num_whitespace++;
|
||||
if (fread(&whitespace, 1, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
fseek(f, num_whitespace, SEEK_SET); //Seek to the place after all whitespace (we may have just read too far).
|
||||
|
||||
char buffer[6];
|
||||
if (fread(buffer, 5, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
@@ -140,15 +173,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
|
||||
+41
-17
@@ -5,6 +5,7 @@
|
||||
#include "gcodeExport.h"
|
||||
#include "gcodePlanner.h"
|
||||
#include "infill.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -16,20 +17,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], PrintFeatureType::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,13 +51,14 @@ 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")]
|
||||
unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
|
||||
max_object_height_per_extruder[extr_nr] =
|
||||
std::max( max_object_height_per_extruder[extr_nr]
|
||||
, mesh.layer_nr_max_filled_layer );
|
||||
}
|
||||
int support_extruder_nr = storage.getSettingAsIndex("support_extruder_nr"); // TODO: support extruder should be configurable per object
|
||||
@@ -72,7 +83,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;
|
||||
}
|
||||
@@ -159,7 +170,7 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
|
||||
{
|
||||
|
||||
int n_patterns = 2; // alternating patterns between layers
|
||||
double infill_overlap = 15; // so that it can't be zero
|
||||
double infill_overlap = 15; // so that it can't be zero; EDIT: wtf?
|
||||
|
||||
generateGroundpoly(storage);
|
||||
|
||||
@@ -170,7 +181,15 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
|
||||
std::vector<Polygons>& patterns = patterns_per_extruder.back();
|
||||
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
|
||||
{
|
||||
generateLineInfill(ground_poly, -line_width/2, patterns[pattern_idx], line_width, line_width, infill_overlap, 45 + pattern_idx*90);
|
||||
Polygons result_polygons; // should remain empty, since we generate lines pattern!
|
||||
Polygons* in_between = nullptr;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
int outline_offset = -line_width/2;
|
||||
int line_distance = line_width;
|
||||
double fill_angle = 45 + pattern_idx * 90;
|
||||
Polygons& result_lines = patterns[pattern_idx];
|
||||
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, avoidOverlappingPerimeters, line_width, line_distance, infill_overlap, fill_angle);
|
||||
infill_comp.generate(result_polygons, result_lines, in_between);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,10 +237,15 @@ void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer
|
||||
GCodePathConfig& config = config_per_extruder[new_extruder];
|
||||
int start_idx = 0; // TODO: figure out which idx is closest to the far right corner
|
||||
gcodeLayer.addPolygon(ground_poly.back(), start_idx, &config);
|
||||
gcodeLayer.addLinesByOptimizer(pattern, &config);
|
||||
gcodeLayer.addLinesByOptimizer(pattern, &config, SpaceFillType::Lines);
|
||||
|
||||
last_prime_tower_poly_printed[new_extruder] = layer_nr;
|
||||
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Support, 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));
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
|
||||
+12
-13
@@ -4,24 +4,23 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
enum class EPrintFeature : unsigned int // unused!!
|
||||
{ // TODO: use in gcodePathConfigs ?
|
||||
OUTER_WALL,
|
||||
INNER_WALLS,
|
||||
INFILL,
|
||||
SKIN,
|
||||
HELPERS,
|
||||
UNCLASSIFIED,
|
||||
ENUM_COUNT
|
||||
enum class PrintFeatureType
|
||||
{
|
||||
NoneType, // unused, but libArcus depends on it
|
||||
OuterWall,
|
||||
InnerWall,
|
||||
Skin,
|
||||
Support,
|
||||
Skirt,
|
||||
Infill,
|
||||
SupportInfill,
|
||||
MoveCombing,
|
||||
MoveRetraction
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // PRINT_FEATURE
|
||||
+6
-6
@@ -64,22 +64,22 @@ void Progress::init()
|
||||
total_timing = accumulated_time;
|
||||
}
|
||||
|
||||
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* command_socket)
|
||||
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max)
|
||||
{
|
||||
float percentage = calcOverallProgress(stage, float(progress_in_stage) / float(progress_in_stage_max));
|
||||
if (command_socket)
|
||||
if (CommandSocket::getInstance())
|
||||
{
|
||||
command_socket->sendProgress(percentage);
|
||||
CommandSocket::getInstance()->sendProgress(percentage);
|
||||
}
|
||||
|
||||
logProgress(names[(int)stage].c_str(), progress_in_stage, progress_in_stage_max, percentage);
|
||||
}
|
||||
|
||||
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper, CommandSocket* command_socket)
|
||||
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper)
|
||||
{
|
||||
if (command_socket)
|
||||
if (CommandSocket::getInstance())
|
||||
{
|
||||
command_socket->sendProgressStage(stage);
|
||||
CommandSocket::getInstance()->sendProgressStage(stage);
|
||||
}
|
||||
|
||||
if (time_keeper)
|
||||
|
||||
+3
-5
@@ -52,22 +52,20 @@ private:
|
||||
public:
|
||||
static void init(); //!< Initialize some values needed in a fast computation of the progress
|
||||
/*!
|
||||
* Message progress over the \p commandSocket and to the terminal (if the command line arg '-p' is provided).
|
||||
* Message progress over the CommandSocket and to the terminal (if the command line arg '-p' is provided).
|
||||
*
|
||||
* \param stage The current stage of processing
|
||||
* \param progress_in_stage Any number giving the progress within the stage
|
||||
* \param progress_in_stage_max The maximal value of \p progress_in_stage
|
||||
* \param commandSocket The command socket over which to communicate the progress.
|
||||
*/
|
||||
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* commandSocket);
|
||||
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max);
|
||||
/*!
|
||||
* Message the progress stage over the command socket.
|
||||
*
|
||||
* \param stage The current stage
|
||||
* \param timeKeeper The stapwatch keeping track of the timings for each stage (optional)
|
||||
* \param commandSocket The command socket over which to communicate (optional)
|
||||
*/
|
||||
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper, CommandSocket* commandSocket);
|
||||
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef SPACE_FILL_TYPE
|
||||
#define SPACE_FILL_TYPE
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Enum class enumerating the strategies with which an area can be occupied with filament
|
||||
*
|
||||
* The walls/perimeters are Polygons
|
||||
* ZigZag infill is PolyLines, and so is following mesh surface mode for non-polygon surfaces
|
||||
* Grid, Triangles and lines infill is Lines
|
||||
*/
|
||||
enum class SpaceFillType
|
||||
{
|
||||
None,
|
||||
Polygons,
|
||||
PolyLines,
|
||||
Lines
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // SPACE_FILL_TYPE
|
||||
+29
-17
@@ -6,11 +6,12 @@
|
||||
|
||||
#include "Progress.h"
|
||||
#include "weaveDataStorage.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
void Weaver::weave(MeshGroup* meshgroup)
|
||||
{
|
||||
wireFrame.meshgroup = meshgroup;
|
||||
|
||||
@@ -28,7 +29,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++)
|
||||
@@ -53,10 +53,17 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
|
||||
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline, 1);
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, 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)
|
||||
@@ -64,10 +71,10 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
else
|
||||
starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, nullptr, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, nullptr);
|
||||
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count, commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
Polygons parts1;
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
@@ -78,8 +85,8 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
|
||||
chainify_polygons(parts1, starting_point_in_layer, chainified, false);
|
||||
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(Inset0Type, layer_idx - starting_layer_idx, chainified, 1);
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, layer_idx - starting_layer_idx, chainified, 1);
|
||||
|
||||
if (chainified.size() > 0)
|
||||
{
|
||||
@@ -101,10 +108,10 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
{
|
||||
Polygons* lower_top_parts = &wireFrame.bottom_outline;
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr);
|
||||
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size(), commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_idx];
|
||||
|
||||
@@ -136,16 +143,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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -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:
|
||||
@@ -60,9 +61,8 @@ public:
|
||||
* Creates a wireframe for the model consisting of horizontal 'flat' parts and connections between consecutive flat parts consisting of UP moves and diagonally DOWN moves.
|
||||
*
|
||||
* \param objects The objects for which to create a wireframe print
|
||||
* \param commandSocket the commandSocket
|
||||
*/
|
||||
void weave(MeshGroup* objects, CommandSocket* commandSocket);
|
||||
void weave(MeshGroup* objects);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
+61
-40
@@ -12,19 +12,27 @@ namespace cura
|
||||
{
|
||||
|
||||
|
||||
void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
void Wireframe2gcode::writeGCode()
|
||||
{
|
||||
|
||||
gcode.preSetup(wireFrame.meshgroup);
|
||||
|
||||
if (commandSocket)
|
||||
commandSocket->beginGCode();
|
||||
if (CommandSocket::getInstance())
|
||||
CommandSocket::getInstance()->beginGCode();
|
||||
|
||||
processStartingCode(commandSocket);
|
||||
processStartingCode();
|
||||
|
||||
int maxObjectHeight = wireFrame.layers.back().z1;
|
||||
int maxObjectHeight;
|
||||
if (wireFrame.layers.empty())
|
||||
{
|
||||
maxObjectHeight = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxObjectHeight = wireFrame.layers.back().z1;
|
||||
}
|
||||
|
||||
processSkirt(commandSocket);
|
||||
processSkirt();
|
||||
|
||||
|
||||
unsigned int total_layers = wireFrame.layers.size();
|
||||
@@ -68,10 +76,10 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
|
||||
}
|
||||
);
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr);
|
||||
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_nr];
|
||||
|
||||
@@ -157,13 +165,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
|
||||
gcode.writeFanCommand(0);
|
||||
|
||||
finalize(maxObjectHeight);
|
||||
|
||||
if (commandSocket)
|
||||
{
|
||||
commandSocket->sendGCodeLayer();
|
||||
commandSocket->endSendSlicedObject();
|
||||
}
|
||||
finalize();
|
||||
}
|
||||
|
||||
|
||||
@@ -231,11 +233,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 +478,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,33 +534,45 @@ 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)
|
||||
void Wireframe2gcode::processStartingCode()
|
||||
{
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
if (!command_socket)
|
||||
if (!CommandSocket::isInstantiated())
|
||||
{
|
||||
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
|
||||
gcode.writeCode(gcode.getFileHeader().c_str());
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -572,30 +589,34 @@ void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
|
||||
}
|
||||
|
||||
|
||||
void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
|
||||
void Wireframe2gcode::processSkirt()
|
||||
{
|
||||
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();
|
||||
|
||||
for (unsigned int poly_idx = 0; poly_idx < skirt.size(); poly_idx++)
|
||||
for (unsigned int poly_order_idx = 0; poly_order_idx < skirt.size(); poly_order_idx++)
|
||||
{
|
||||
unsigned int actual_poly_idx = order.polyOrder[poly_idx];
|
||||
PolygonRef poly = skirt[actual_poly_idx];
|
||||
gcode.writeMove(poly[order.polyStart[actual_poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
unsigned int poly_idx = order.polyOrder[poly_order_idx];
|
||||
PolygonRef poly = skirt[poly_idx];
|
||||
gcode.writeMove(poly[order.polyStart[poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
Point& p = poly[(point_idx + order.polyStart[actual_poly_idx] + 1) % poly.size()];
|
||||
Point& p = poly[(point_idx + order.polyStart[poly_idx] + 1) % poly.size()];
|
||||
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_speed"), getSettingInMillimetersPerSecond("skirt_line_width"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
@@ -69,26 +71,26 @@ public:
|
||||
|
||||
Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base);
|
||||
|
||||
void writeGCode(CommandSocket* commandSocket);
|
||||
void writeGCode();
|
||||
|
||||
|
||||
private:
|
||||
WireFrame wireFrame;
|
||||
WireFrame& wireFrame;
|
||||
|
||||
/*!
|
||||
* Startup gcode: nozzle temp up, retraction settings, bed temp
|
||||
*/
|
||||
void processStartingCode(CommandSocket* command_socket);
|
||||
void processStartingCode();
|
||||
|
||||
/*!
|
||||
* Lay down a skirt
|
||||
*/
|
||||
void processSkirt(CommandSocket* commandSocket);
|
||||
void processSkirt();
|
||||
|
||||
/*!
|
||||
* 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
|
||||
|
||||
+5
-47
@@ -9,50 +9,7 @@
|
||||
|
||||
namespace cura {
|
||||
|
||||
bool Comb::moveInsideBoundary(Point* p, int distance)
|
||||
{
|
||||
return PolygonUtils::moveInside(boundary_inside, *p, distance) != NO_INDEX;
|
||||
}
|
||||
|
||||
Polygons Comb::getLayerSecondWalls()
|
||||
{
|
||||
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)
|
||||
{
|
||||
for (SliceLayerPart& part : mesh.layers[layer_nr].parts)
|
||||
{
|
||||
// we want the 2nd inner walls
|
||||
if (part.insets.size() >= 2) {
|
||||
layer_walls.add(part.insets[1]);
|
||||
continue;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// and if there is no walls, we'll try to move inside from the outline
|
||||
Polygons newOutline = part.outline.offset(-offset_from_outlines);
|
||||
if(newOutline.polygonLength() > 0) {
|
||||
layer_walls.add(newOutline);
|
||||
continue;
|
||||
}
|
||||
// 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 layer_walls;
|
||||
}
|
||||
}
|
||||
|
||||
// boundary_outside is only computed when it's needed!
|
||||
Polygons* Comb::getBoundaryOutside()
|
||||
{
|
||||
@@ -64,7 +21,7 @@ Polygons* Comb::getBoundaryOutside()
|
||||
return boundary_outside;
|
||||
}
|
||||
|
||||
Comb::Comb(SliceDataStorage& storage, 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
|
||||
@@ -72,7 +29,7 @@ Comb::Comb(SliceDataStorage& storage, int layer_nr, int64_t comb_boundary_offset
|
||||
, 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 !!
|
||||
{
|
||||
@@ -98,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
|
||||
@@ -114,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
|
||||
@@ -428,6 +385,7 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
|
||||
}
|
||||
}
|
||||
}
|
||||
optimized_comb_path.push_back(comb_path.back());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+3
-20
@@ -216,14 +216,9 @@ private:
|
||||
|
||||
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.
|
||||
@@ -235,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, 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,16 +250,6 @@ public:
|
||||
* \return Whether combing has succeeded; otherwise a retraction is needed.
|
||||
*/
|
||||
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false, int64_t max_comb_distance_ignored = MM2INT(1.5));
|
||||
|
||||
/*!
|
||||
* 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);
|
||||
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+202
-126
@@ -7,42 +7,68 @@
|
||||
#include <cinttypes>
|
||||
|
||||
#include <Arcus/Socket.h>
|
||||
#include <Arcus/SocketListener.h>
|
||||
#include <Arcus/Error.h>
|
||||
|
||||
#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
|
||||
|
||||
|
||||
CommandSocket* CommandSocket::instance = nullptr; // instantiate instance
|
||||
|
||||
class Listener : public Arcus::SocketListener
|
||||
{
|
||||
public:
|
||||
void stateChanged(Arcus::SocketState::SocketState newState) override
|
||||
{
|
||||
}
|
||||
|
||||
void messageReceived() override
|
||||
{
|
||||
}
|
||||
|
||||
void error(const Arcus::Error & error) override
|
||||
{
|
||||
if(error.getErrorCode() == Arcus::ErrorCode::Debug)
|
||||
{
|
||||
log("%s\n", error.toString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("%s\n", error.toString().c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CommandSocket::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: socket(nullptr)
|
||||
, object_count(0)
|
||||
, current_sliced_object(nullptr)
|
||||
, sliced_objects(0)
|
||||
, current_layer_count(0)
|
||||
, current_layer_offset(0)
|
||||
{ }
|
||||
|
||||
cura::proto::Layer* getLayerById(int id);
|
||||
std::shared_ptr<cura::proto::Layer> getLayerById(int id);
|
||||
|
||||
Arcus::Socket* socket;
|
||||
|
||||
// Number of objects that need to be sliced
|
||||
int object_count;
|
||||
|
||||
// Message that holds a list of sliced objects
|
||||
std::shared_ptr<cura::proto::SlicedObjectList> sliced_object_list;
|
||||
|
||||
// Message that holds the currently sliced object (to be added to sliced_object_list)
|
||||
cura::proto::SlicedObject* current_sliced_object;
|
||||
|
||||
|
||||
// Number of sliced objects for this sliced object list
|
||||
int sliced_objects;
|
||||
|
||||
@@ -50,65 +76,70 @@ public:
|
||||
// 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;
|
||||
|
||||
std::string temp_gcode_file;
|
||||
std::ostringstream gcode_output_stream;
|
||||
|
||||
// Print object that olds one or more meshes that need to be sliced.
|
||||
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
|
||||
|
||||
std::unordered_map<int, std::shared_ptr<cura::proto::Layer>> sliced_layers;
|
||||
};
|
||||
|
||||
CommandSocket::CommandSocket()
|
||||
: d(new Private)
|
||||
: private_data(new Private)
|
||||
{
|
||||
FffProcessor::getInstance()->setCommandSocket(this);
|
||||
}
|
||||
|
||||
CommandSocket* CommandSocket::getInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
void CommandSocket::instantiate()
|
||||
{
|
||||
instance = new CommandSocket();
|
||||
}
|
||||
|
||||
bool CommandSocket::isInstantiated()
|
||||
{
|
||||
return instance != nullptr;
|
||||
}
|
||||
|
||||
|
||||
void CommandSocket::connect(const std::string& ip, int port)
|
||||
{
|
||||
d->socket = new Arcus::Socket();
|
||||
//d->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
|
||||
d->socket->registerMessageType(1, &cura::proto::Slice::default_instance());
|
||||
d->socket->registerMessageType(2, &cura::proto::SlicedObjectList::default_instance());
|
||||
d->socket->registerMessageType(3, &cura::proto::Progress::default_instance());
|
||||
d->socket->registerMessageType(4, &cura::proto::GCodeLayer::default_instance());
|
||||
d->socket->registerMessageType(5, &cura::proto::ObjectPrintTime::default_instance());
|
||||
d->socket->registerMessageType(6, &cura::proto::SettingList::default_instance());
|
||||
d->socket->registerMessageType(7, &cura::proto::GCodePrefix::default_instance());
|
||||
private_data->socket = new Arcus::Socket();
|
||||
private_data->socket->addListener(new Listener());
|
||||
|
||||
d->socket->connect(ip, port);
|
||||
//private_data->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::Slice::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::Layer::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::Progress::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::GCodeLayer::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::ObjectPrintTime::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::SettingList::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::GCodePrefix::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::SlicingFinished::default_instance());
|
||||
|
||||
private_data->socket->connect(ip, port);
|
||||
|
||||
log("Connecting to %s:%i\n", ip.c_str(), port);
|
||||
|
||||
while(private_data->socket->getState() != Arcus::SocketState::Connected && private_data->socket->getState() != Arcus::SocketState::Error)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
log("Connected to %s:%i\n", ip.c_str(), 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(private_data->socket->getState() != Arcus::SocketState::Closed && private_data->socket->getState() != 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)
|
||||
{
|
||||
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
|
||||
{
|
||||
logError("Slicing mesh group failed!");
|
||||
}
|
||||
}
|
||||
d->objects_to_slice.clear();
|
||||
FffProcessor::getInstance()->finalize();
|
||||
sendGCodeLayer();
|
||||
sendPrintTime();
|
||||
//TODO: Support all-at-once/one-at-a-time printing
|
||||
//d->processor->processModel(d->object_to_slice.get());
|
||||
//d->object_to_slice.reset();
|
||||
//d->processor->resetFileNumber();
|
||||
|
||||
//sendPrintTime();
|
||||
}
|
||||
|
||||
// Actually start handling messages.
|
||||
Arcus::MessagePtr message = d->socket->takeNextMessage();
|
||||
Arcus::MessagePtr message = private_data->socket->takeNextMessage();
|
||||
cura::proto::SettingList* setting_list = dynamic_cast<cura::proto::SettingList*>(message.get());
|
||||
if(setting_list)
|
||||
{
|
||||
@@ -125,38 +156,93 @@ void CommandSocket::connect(const std::string& ip, int port)
|
||||
if(slice)
|
||||
{
|
||||
// Reset object counts
|
||||
d->object_count = 0;
|
||||
d->object_ids.clear();
|
||||
private_data->object_count = 0;
|
||||
for(auto object : slice->object_lists())
|
||||
{
|
||||
handleObjectList(&object);
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
|
||||
if(!d->socket->errorString().empty())
|
||||
//If there is an object to slice, do so.
|
||||
if(private_data->objects_to_slice.size())
|
||||
{
|
||||
logError("%s\n", d->socket->errorString().data());
|
||||
d->socket->clearError();
|
||||
FffProcessor::getInstance()->resetFileNumber();
|
||||
for(auto object : private_data->objects_to_slice)
|
||||
{
|
||||
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
|
||||
{
|
||||
logError("Slicing mesh group failed!");
|
||||
}
|
||||
}
|
||||
private_data->objects_to_slice.clear();
|
||||
FffProcessor::getInstance()->finalize();
|
||||
flushGcode();
|
||||
sendPrintTime();
|
||||
sendFinishedSlicing();
|
||||
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
|
||||
//TODO: Support all-at-once/one-at-a-time printing
|
||||
//private_data->processor->processModel(private_data->object_to_slice.get());
|
||||
//private_data->object_to_slice.reset();
|
||||
//private_data->processor->resetFileNumber();
|
||||
|
||||
//sendPrintTime();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
|
||||
log("Closing connection\n");
|
||||
private_data->socket->close();
|
||||
}
|
||||
|
||||
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
{
|
||||
if(list->objects_size() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
//private_data->object_count = 0;
|
||||
//private_data->object_ids.clear();
|
||||
private_data->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
|
||||
MeshGroup* meshgroup = private_data->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();
|
||||
|
||||
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
|
||||
int face_count = object.vertices().size() / bytes_per_face;
|
||||
|
||||
if(face_count <= 0)
|
||||
{
|
||||
logWarning("Got an empty mesh, ignoring it!");
|
||||
continue;
|
||||
}
|
||||
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();
|
||||
|
||||
for(int i = 0; i < face_count; ++i)
|
||||
{
|
||||
//TODO: Apply matrix
|
||||
@@ -168,24 +254,26 @@ 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());
|
||||
}
|
||||
|
||||
d->object_ids.push_back(object.id());
|
||||
mesh.finish();
|
||||
}
|
||||
|
||||
for(auto setting : list->settings())
|
||||
{
|
||||
object_to_slice->setSetting(setting.name(), setting.value());
|
||||
}
|
||||
|
||||
d->object_count++;
|
||||
object_to_slice->finalize();
|
||||
private_data->object_count++;
|
||||
meshgroup->finalize();
|
||||
}
|
||||
|
||||
void CommandSocket::handleSettingList(cura::proto::SettingList* list)
|
||||
@@ -198,29 +286,21 @@ void CommandSocket::handleSettingList(cura::proto::SettingList* list)
|
||||
|
||||
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
|
||||
{
|
||||
if(!d->current_sliced_object)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cura::proto::Layer* layer = d->getLayerById(layer_nr);
|
||||
std::shared_ptr<cura::proto::Layer> layer = private_data->getLayerById(layer_nr);
|
||||
layer->set_height(z);
|
||||
layer->set_thickness(height);
|
||||
}
|
||||
|
||||
void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
{
|
||||
if(!d->current_sliced_object)
|
||||
return;
|
||||
|
||||
if (polygons.size() == 0)
|
||||
return;
|
||||
|
||||
cura::proto::Layer* layer = d->getLayerById(layer_nr);
|
||||
std::shared_ptr<cura::proto::Layer> proto_layer = private_data->getLayerById(layer_nr);
|
||||
|
||||
for(unsigned int i = 0; i < polygons.size(); ++i)
|
||||
{
|
||||
cura::proto::Polygon* p = layer->add_polygons();
|
||||
cura::proto::Polygon* p = proto_layer->add_polygons();
|
||||
p->set_type(static_cast<cura::proto::Polygon_Type>(type));
|
||||
std::string polydata;
|
||||
polydata.append(reinterpret_cast<const char*>(polygons[i].data()), polygons[i].size() * sizeof(Point));
|
||||
@@ -232,10 +312,10 @@ void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polyg
|
||||
void CommandSocket::sendProgress(float amount)
|
||||
{
|
||||
auto message = std::make_shared<cura::proto::Progress>();
|
||||
amount /= d->object_count;
|
||||
amount += d->sliced_objects * (1. / d->object_count);
|
||||
amount /= private_data->object_count;
|
||||
amount += private_data->sliced_objects * (1. / private_data->object_count);
|
||||
message->set_amount(amount);
|
||||
d->socket->sendMessage(message);
|
||||
private_data->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
void CommandSocket::sendProgressStage(Progress::Stage stage)
|
||||
@@ -248,7 +328,7 @@ void CommandSocket::sendPrintTime()
|
||||
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);
|
||||
private_data->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
|
||||
@@ -260,75 +340,71 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
|
||||
// socket.sendFloat32(print_time);
|
||||
}
|
||||
|
||||
void CommandSocket::beginSendSlicedObject()
|
||||
void CommandSocket::sendLayerData()
|
||||
{
|
||||
if(!d->sliced_object_list)
|
||||
{
|
||||
d->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
|
||||
}
|
||||
private_data->sliced_objects++;
|
||||
private_data->current_layer_offset = private_data->current_layer_count;
|
||||
log("End sliced object called. Sending ", private_data->current_layer_count, " layers.");
|
||||
|
||||
d->current_sliced_object = d->sliced_object_list->add_objects();
|
||||
d->current_sliced_object->set_id(d->object_ids[d->sliced_objects]);
|
||||
if(private_data->sliced_objects >= private_data->object_count)
|
||||
{
|
||||
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : private_data->sliced_layers) //Note: This is in no particular order!
|
||||
{
|
||||
private_data->socket->sendMessage(entry.second); //Send the actual layers.
|
||||
}
|
||||
private_data->sliced_objects = 0;
|
||||
private_data->current_layer_count = 0;
|
||||
private_data->current_layer_offset = 0;
|
||||
private_data->sliced_layers.clear();
|
||||
auto done_message = std::make_shared<cura::proto::SlicingFinished>();
|
||||
private_data->socket->sendMessage(done_message);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandSocket::endSendSlicedObject()
|
||||
void CommandSocket::sendFinishedSlicing()
|
||||
{
|
||||
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;
|
||||
}
|
||||
std::shared_ptr<cura::proto::SlicingFinished> done_message = std::make_shared<cura::proto::SlicingFinished>();
|
||||
private_data->socket->sendMessage(done_message);
|
||||
}
|
||||
|
||||
void CommandSocket::beginGCode()
|
||||
{
|
||||
FffProcessor::getInstance()->setTargetStream(&d->gcode_output_stream);
|
||||
FffProcessor::getInstance()->setTargetStream(&private_data->gcode_output_stream);
|
||||
}
|
||||
|
||||
void CommandSocket::sendGCodeLayer()
|
||||
void CommandSocket::flushGcode()
|
||||
{
|
||||
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);
|
||||
message->set_data(private_data->gcode_output_stream.str());
|
||||
private_data->socket->sendMessage(message);
|
||||
|
||||
d->gcode_output_stream.str("");
|
||||
private_data->gcode_output_stream.str("");
|
||||
}
|
||||
|
||||
void CommandSocket::sendGCodePrefix(std::string prefix)
|
||||
{
|
||||
auto message = std::make_shared<cura::proto::GCodePrefix>();
|
||||
message->set_data(prefix);
|
||||
d->socket->sendMessage(message);
|
||||
private_data->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
|
||||
std::shared_ptr<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; });
|
||||
auto itr = sliced_layers.find(id);
|
||||
|
||||
cura::proto::Layer* layer = nullptr;
|
||||
if(itr != current_sliced_object->mutable_layers()->end())
|
||||
std::shared_ptr<cura::proto::Layer> layer;
|
||||
if(itr != sliced_layers.end())
|
||||
{
|
||||
layer = &(*itr);
|
||||
layer = itr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
layer = current_sliced_object->add_layers();
|
||||
layer = std::make_shared<cura::proto::Layer>();
|
||||
layer->set_id(id);
|
||||
current_layer_count++;
|
||||
sliced_layers[id] = layer;
|
||||
}
|
||||
|
||||
return layer;
|
||||
|
||||
+41
-8
@@ -5,6 +5,7 @@
|
||||
#include "utils/polygon.h"
|
||||
#include "settings.h"
|
||||
#include "Progress.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -15,8 +16,18 @@ namespace cura {
|
||||
|
||||
class CommandSocket
|
||||
{
|
||||
private:
|
||||
static CommandSocket* instance; //!< May be a nullptr in case it hasn't been instantiated.
|
||||
|
||||
CommandSocket(); //!< The single constructor is known only privately, since this class is similar to a singleton class (except the single object doesn't need to be instantiated)
|
||||
|
||||
public:
|
||||
CommandSocket();
|
||||
static CommandSocket* getInstance(); //!< Get the CommandSocket instance, or nullptr if it hasn't been instantiated.
|
||||
|
||||
static void instantiate(); //!< Instantiate the CommandSocket.
|
||||
|
||||
static bool isInstantiated(); //!< Check whether the singleton is instantiated
|
||||
|
||||
/*!
|
||||
* Connect with the GUI
|
||||
* This creates and initialises the arcus socket and then continues listening for messages.
|
||||
@@ -38,15 +49,20 @@ public:
|
||||
void handleSettingList(cura::proto::SettingList* list);
|
||||
|
||||
/*!
|
||||
* Does nothing at the moment
|
||||
* Send info on a layer to be displayed by the forntend: set the z and the thickness of the layer.
|
||||
*/
|
||||
void sendLayerInfo(int layer_nr, int32_t z, int32_t height);
|
||||
|
||||
/*!
|
||||
* Send a polygon to the engine. This is used for the layerview in the GUI
|
||||
*/
|
||||
void sendPolygons(cura::PolygonType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
void sendPolygons(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
/*!
|
||||
* Send a polygon to the engine if the command socket is instantiated. This is used for the layerview in the GUI
|
||||
*/
|
||||
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
/*!
|
||||
* Send progress to GUI
|
||||
*/
|
||||
@@ -66,17 +82,34 @@ public:
|
||||
* Does nothing at the moment
|
||||
*/
|
||||
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
|
||||
|
||||
/*!
|
||||
* Send the sliced layer data to the GUI.
|
||||
*
|
||||
* The GUI may use this to visualise the g-code, so that the user can
|
||||
* inspect the result of slicing.
|
||||
*/
|
||||
void sendLayerData();
|
||||
|
||||
void beginSendSlicedObject();
|
||||
void endSendSlicedObject();
|
||||
/*!
|
||||
* \brief Sends a message to indicate that all the slicing is done.
|
||||
*
|
||||
* This should indicate that no more data (g-code, prefix/postfix, metadata
|
||||
* or otherwise) should be sent any more regarding the latest slice job.
|
||||
*/
|
||||
void sendFinishedSlicing();
|
||||
|
||||
void beginGCode();
|
||||
void sendGCodeLayer();
|
||||
|
||||
/*!
|
||||
* Flush the gcode in gcode_output_stream into a message queued in the socket.
|
||||
*/
|
||||
void flushGcode();
|
||||
void sendGCodePrefix(std::string prefix);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
const std::unique_ptr<Private> d;
|
||||
const std::unique_ptr<Private> private_data;
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+370
-218
@@ -1,26 +1,27 @@
|
||||
/** 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"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
GCodeExport::GCodeExport()
|
||||
: output_stream(&std::cout),currentPosition(0,0,0),startPosition(INT32_MIN,INT32_MIN,0),commandSocket(nullptr),layer_nr(0)
|
||||
: output_stream(&std::cout)
|
||||
, currentPosition(0,0,MM2INT(20))
|
||||
, 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 = 0;
|
||||
last_coasted_amount_mm3 = 0;
|
||||
setFlavor(EGCodeFlavor::REPRAP);
|
||||
}
|
||||
|
||||
@@ -28,8 +29,24 @@ GCodeExport::~GCodeExport()
|
||||
{
|
||||
}
|
||||
|
||||
void GCodeExport::setCommandSocketAndLayerNr(CommandSocket* commandSocket_, unsigned int layer_nr_) {
|
||||
commandSocket = commandSocket_;
|
||||
std::string GCodeExport::getFileHeader(double print_time, int filament_used_0, int filament_used_1)
|
||||
{
|
||||
std::ostringstream prefix;
|
||||
prefix << ";FLAVOR:" << toString(flavor) << new_line;
|
||||
prefix << ";TIME:" << int(print_time) << new_line;
|
||||
if (flavor == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
prefix << ";MATERIAL:" << int(filament_used_0) << new_line;
|
||||
prefix << ";MATERIAL2:" << int(filament_used_1) << new_line;
|
||||
|
||||
prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(0))) << new_line;
|
||||
// prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(1))) << new_line; // TODO: the second nozzle size isn't always initiated!
|
||||
}
|
||||
return prefix.str();
|
||||
}
|
||||
|
||||
|
||||
void GCodeExport::setLayerNr(unsigned int layer_nr_) {
|
||||
layer_nr = layer_nr_;
|
||||
}
|
||||
|
||||
@@ -39,6 +56,11 @@ void GCodeExport::setOutputStream(std::ostream* stream)
|
||||
*output_stream << std::fixed;
|
||||
}
|
||||
|
||||
int GCodeExport::getNozzleSize(int extruder_idx)
|
||||
{
|
||||
return extruder_attr[extruder_idx].nozzle_size;
|
||||
}
|
||||
|
||||
Point GCodeExport::getExtruderOffset(int id)
|
||||
{
|
||||
return extruder_attr[id].nozzle_offset;
|
||||
@@ -68,6 +90,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()
|
||||
@@ -94,17 +125,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;
|
||||
@@ -117,20 +137,57 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
double GCodeExport::eToMm(double e)
|
||||
{
|
||||
if (is_volumatric)
|
||||
{
|
||||
return e / extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
else
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
double GCodeExport::mm3ToE(double mm3)
|
||||
{
|
||||
if (is_volumatric)
|
||||
{
|
||||
return mm3;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mm3 / extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
}
|
||||
|
||||
double GCodeExport::mmToE(double mm)
|
||||
{
|
||||
if (is_volumatric)
|
||||
{
|
||||
return mm * extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mm;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +195,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -155,7 +212,7 @@ void GCodeExport::resetTotalPrintTimeAndFilament()
|
||||
extruder_attr[e].totalFilament = 0.0;
|
||||
extruder_attr[e].currentTemperature = 0;
|
||||
}
|
||||
extrusion_amount = 0.0;
|
||||
current_e_value = 0.0;
|
||||
estimateCalculator.reset();
|
||||
}
|
||||
|
||||
@@ -177,43 +234,82 @@ void GCodeExport::writeComment(std::string comment)
|
||||
*output_stream << comment[i];
|
||||
}
|
||||
}
|
||||
*output_stream << "\n";
|
||||
*output_stream << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::writeTypeComment(const char* type)
|
||||
{
|
||||
*output_stream << ";TYPE:" << type << "\n";
|
||||
*output_stream << ";TYPE:" << type << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::writeTypeComment(PrintFeatureType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PrintFeatureType::OuterWall:
|
||||
*output_stream << ";TYPE:WALL-OUTER" << new_line;
|
||||
break;
|
||||
case PrintFeatureType::InnerWall:
|
||||
*output_stream << ";TYPE:WALL-INNER" << new_line;
|
||||
break;
|
||||
case PrintFeatureType::Skin:
|
||||
*output_stream << ";TYPE:SKIN" << new_line;
|
||||
break;
|
||||
case PrintFeatureType::Support:
|
||||
*output_stream << ";TYPE:SUPPORT" << new_line;
|
||||
break;
|
||||
case PrintFeatureType::Skirt:
|
||||
*output_stream << ";TYPE:SKIRT" << new_line;
|
||||
break;
|
||||
case PrintFeatureType::Infill:
|
||||
*output_stream << ";TYPE:FILL" << new_line;
|
||||
break;
|
||||
case PrintFeatureType::SupportInfill:
|
||||
*output_stream << ";TYPE:SUPPORT" << new_line;
|
||||
break;
|
||||
case PrintFeatureType::MoveCombing:
|
||||
case PrintFeatureType::MoveRetraction:
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GCodeExport::writeLayerComment(int layer_nr)
|
||||
{
|
||||
*output_stream << ";LAYER:" << layer_nr << "\n";
|
||||
*output_stream << ";LAYER:" << layer_nr << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::writeLayerCountComment(int layer_count)
|
||||
{
|
||||
*output_stream << ";LAYER_COUNT:" << layer_count << "\n";
|
||||
*output_stream << ";LAYER_COUNT:" << layer_count << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::writeLine(const char* line)
|
||||
{
|
||||
*output_stream << line << "\n";
|
||||
*output_stream << line << new_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;
|
||||
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0" << new_line;
|
||||
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";
|
||||
*output_stream << "G4 P" << int(timeAmount * 1000) << new_line;
|
||||
estimateCalculator.addTime(timeAmount);
|
||||
}
|
||||
|
||||
@@ -227,236 +323,297 @@ 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 = mm3ToE(extrusion_mm3_per_mm);
|
||||
|
||||
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 << new_line;
|
||||
currentSpeed = double(rpm);
|
||||
}
|
||||
//Add M101 or M201 to enable the proper extruder.
|
||||
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << new_line;
|
||||
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" << new_line;
|
||||
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 << new_line;
|
||||
|
||||
currentPosition = Point3(x, y, z);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(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;
|
||||
|
||||
|
||||
#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 = mm3ToE(extrusion_mm3_per_mm);
|
||||
|
||||
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) << new_line;
|
||||
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 += mm3ToE(prime_volume);
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
{
|
||||
if (firmware_retract)
|
||||
{ // note that BFB is handled differently
|
||||
*output_stream << "G11" << new_line;
|
||||
//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 << new_line;
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
}
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(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 << new_line;
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(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)
|
||||
{
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(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)
|
||||
{
|
||||
Point3 diff = Point3(x,y,z) - getPosition();
|
||||
if (isZHopped > 0)
|
||||
{
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
|
||||
isZHopped = 0;
|
||||
}
|
||||
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";
|
||||
currentSpeed = retractionPrimeSpeed;
|
||||
}
|
||||
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";
|
||||
currentSpeed = retractionPrimeSpeed;
|
||||
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 (commandSocket) {
|
||||
// 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(isRetracted ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, isRetracted ? MM2INT(0.2) : MM2INT(0.1));
|
||||
}
|
||||
}
|
||||
*output_stream << "G0";
|
||||
|
||||
if (currentSpeed != speed)
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
*output_stream << " F" << (speed * 60);
|
||||
currentSpeed = speed;
|
||||
// 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::getInstance()->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
|
||||
}
|
||||
|
||||
*output_stream << std::setprecision(3) <<
|
||||
" X" << INT2MM(gcode_pos.X) <<
|
||||
" Y" << INT2MM(gcode_pos.Y);
|
||||
if (z != currentPosition.z)
|
||||
*output_stream << " Z" << INT2MM(z + isZHopped);
|
||||
if (extrusion_mm3_per_mm > 0.000001)
|
||||
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount;
|
||||
*output_stream << "\n";
|
||||
}
|
||||
|
||||
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 << new_line;
|
||||
|
||||
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), eToMm(current_e_value)), speed);
|
||||
}
|
||||
|
||||
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
{
|
||||
if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction.
|
||||
{
|
||||
return;
|
||||
if (isRetracted)
|
||||
}
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current == mmToE(config->distance))
|
||||
{
|
||||
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)
|
||||
}
|
||||
if (config->distance <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (config->primeAmount > 0)
|
||||
{
|
||||
extrusion_amount += config->primeAmount;
|
||||
{ // 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
|
||||
&& 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 + 1)
|
||||
{
|
||||
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 = mmToE(config->distance);
|
||||
if (firmware_retract)
|
||||
{
|
||||
*output_stream << "G10\n";
|
||||
*output_stream << "G10" << new_line;
|
||||
//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), eToMm(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 << new_line;
|
||||
currentSpeed = config->speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(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)
|
||||
{
|
||||
isZHopped = config->zHop;
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << "\n";
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << new_line;
|
||||
}
|
||||
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)
|
||||
*output_stream << "M103\r\n";
|
||||
if (!extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
*output_stream << "M103" << new_line;
|
||||
|
||||
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 = mmToE(extruder_attr[current_extruder].extruder_switch_retraction_distance);
|
||||
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" << new_line;
|
||||
}
|
||||
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 << new_line;
|
||||
// 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";
|
||||
{
|
||||
*output_stream << "M135 T" << current_extruder << new_line;
|
||||
}
|
||||
else
|
||||
*output_stream << "T" << current_extruder << "\n";
|
||||
{
|
||||
*output_stream << "T" << current_extruder << new_line;
|
||||
}
|
||||
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.
|
||||
@@ -465,11 +622,7 @@ void GCodeExport::switchExtruder(int new_extruder)
|
||||
|
||||
void GCodeExport::writeCode(const char* str)
|
||||
{
|
||||
*output_stream << str;
|
||||
if (flavor == EGCodeFlavor::BFB)
|
||||
*output_stream << "\r\n";
|
||||
else
|
||||
*output_stream << "\n";
|
||||
*output_stream << str << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::writeFanCommand(double speed)
|
||||
@@ -479,16 +632,16 @@ void GCodeExport::writeFanCommand(double speed)
|
||||
if (speed > 0)
|
||||
{
|
||||
if (flavor == EGCodeFlavor::MAKERBOT)
|
||||
*output_stream << "M126 T0\n"; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
|
||||
*output_stream << "M126 T0" << new_line; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
|
||||
else
|
||||
*output_stream << "M106 S" << (speed * 255 / 100) << "\n";
|
||||
*output_stream << "M106 S" << (speed * 255 / 100) << new_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flavor == EGCodeFlavor::MAKERBOT)
|
||||
*output_stream << "M127 T0\n";
|
||||
*output_stream << "M127 T0" << new_line;
|
||||
else
|
||||
*output_stream << "M107\n";
|
||||
*output_stream << "M107" << new_line;
|
||||
}
|
||||
currentFanSpeed = speed;
|
||||
}
|
||||
@@ -504,7 +657,7 @@ void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool
|
||||
*output_stream << "M104";
|
||||
if (extruder != current_extruder)
|
||||
*output_stream << " T" << extruder;
|
||||
*output_stream << " S" << temperature << "\n";
|
||||
*output_stream << " S" << temperature << new_line;
|
||||
extruder_attr[extruder].currentTemperature = temperature;
|
||||
}
|
||||
|
||||
@@ -514,14 +667,12 @@ void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
|
||||
*output_stream << "M190 S";
|
||||
else
|
||||
*output_stream << "M140 S";
|
||||
*output_stream << temperature << "\n";
|
||||
*output_stream << temperature << new_line;
|
||||
}
|
||||
|
||||
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)));
|
||||
@@ -532,3 +683,4 @@ void GCodeExport::finalize(int maxObjectHeight, double moveSpeed, const char* en
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
+178
-72
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "settings.h"
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/NoCopy.h"
|
||||
#include "timeEstimate.h"
|
||||
#include "MeshGroup.h"
|
||||
#include "commandSocket.h"
|
||||
@@ -17,25 +18,21 @@ namespace cura {
|
||||
struct CoastingConfig
|
||||
{
|
||||
bool coasting_enable;
|
||||
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;
|
||||
double coasting_volume;
|
||||
double coasting_speed;
|
||||
double coasting_min_volume;
|
||||
};
|
||||
|
||||
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,52 +40,81 @@ public:
|
||||
class GCodePathConfig
|
||||
{
|
||||
private:
|
||||
double speed; //!< movement speed
|
||||
double speed_iconic; //!< movement speed (mm/s) specific to this print feature
|
||||
double speed; //!< 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;
|
||||
bool spiralize;
|
||||
PrintFeatureType type; //!< name of the feature type
|
||||
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) {}
|
||||
|
||||
void setSpeed(double speed)
|
||||
|
||||
GCodePathConfig(RetractionConfig* retraction_config, PrintFeatureType type)
|
||||
: speed_iconic(0)
|
||||
, speed(0)
|
||||
, line_width(0)
|
||||
, extrusion_mm3_per_mm(0.0)
|
||||
, type(type)
|
||||
, retraction_config(retraction_config)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* 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_iconic = speed;
|
||||
this->speed = speed;
|
||||
}
|
||||
|
||||
void setLineWidth(int line_width)
|
||||
{
|
||||
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 = (speed_iconic*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Set the speed to the iconic speed, i.e. the normal speed of the feature type for which this is a config.
|
||||
*/
|
||||
void setSpeedIconic()
|
||||
{
|
||||
speed = speed_iconic;
|
||||
}
|
||||
|
||||
/*!
|
||||
* 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;
|
||||
@@ -98,10 +124,15 @@ public:
|
||||
{
|
||||
return line_width;
|
||||
}
|
||||
|
||||
int getLayerHeight()
|
||||
|
||||
bool isTravelPath()
|
||||
{
|
||||
return layer_thickness;
|
||||
return line_width == 0;
|
||||
}
|
||||
|
||||
double getFlowPercentage()
|
||||
{
|
||||
return flow;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -113,52 +144,62 @@ 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
|
||||
{
|
||||
int nozzle_size; //!< The nozzle size label of the nozzle (e.g. 0.4mm; irrespective of tolerances)
|
||||
Point nozzle_offset;
|
||||
char extruderCharacter;
|
||||
std::string start_code;
|
||||
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(0.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
|
||||
Point3 currentPosition;
|
||||
Point3 startPosition;
|
||||
double currentSpeed;
|
||||
int zPos;
|
||||
bool isRetracted;
|
||||
int isZHopped;
|
||||
|
||||
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;
|
||||
std::ostream* output_stream;
|
||||
std::string new_line;
|
||||
|
||||
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
|
||||
Point3 currentPosition;
|
||||
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)
|
||||
|
||||
int current_extruder;
|
||||
int currentFanSpeed;
|
||||
EGCodeFlavor flavor;
|
||||
@@ -167,20 +208,65 @@ private:
|
||||
TimeEstimateCalculator estimateCalculator;
|
||||
|
||||
bool is_volumatric;
|
||||
bool firmware_retract; //!< whether retractions are done in the firmware, or hardcoded in E values.
|
||||
|
||||
// for sending jump data
|
||||
CommandSocket* commandSocket;
|
||||
unsigned int layer_nr;
|
||||
unsigned int layer_nr; //!< for sending travel data
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Convert an E value to a value in mm (if it wasn't already in mm) for the current extruder.
|
||||
*
|
||||
* E values are either in mm or in mm^3
|
||||
* The current extruder is used to determine the filament area to make the conversion.
|
||||
*
|
||||
* \param e the value to convert
|
||||
* \return the value converted to mm
|
||||
*/
|
||||
double eToMm(double e);
|
||||
|
||||
/*!
|
||||
* Convert a volume value to an E value (which might be volumetric as well) for the current extruder.
|
||||
*
|
||||
* E values are either in mm or in mm^3
|
||||
* The current extruder is used to determine the filament area to make the conversion.
|
||||
*
|
||||
* \param mm3 the value to convert
|
||||
* \return the value converted to mm or mm3 depending on whether the E axis is volumetric
|
||||
*/
|
||||
double mm3ToE(double mm3);
|
||||
|
||||
/*!
|
||||
* Convert a distance value to an E value (which might be linear/distance based as well) for the current extruder.
|
||||
*
|
||||
* E values are either in mm or in mm^3
|
||||
* The current extruder is used to determine the filament area to make the conversion.
|
||||
*
|
||||
* \param mm the value to convert
|
||||
* \return the value converted to mm or mm3 depending on whether the E axis is volumetric
|
||||
*/
|
||||
double mmToE(double mm);
|
||||
|
||||
public:
|
||||
|
||||
GCodeExport();
|
||||
~GCodeExport();
|
||||
|
||||
void setCommandSocketAndLayerNr(CommandSocket* commandSocket, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Get the gcode file header (e.g. ";FLAVOR:UltiGCode\n")
|
||||
*
|
||||
* \param print_time The total print time of the whole file (if known)
|
||||
* \param filament_used_0 The total mm^3 filament used for the primary extruder (if known)
|
||||
* \param filament_used_1 The total mm^3 filament used for the secondary extruder (if used and if known)
|
||||
* \return The string representing the file header
|
||||
*/
|
||||
std::string getFileHeader(double print_time = 666, int filament_used_0 = 666, int filament_used_1 = 0);
|
||||
|
||||
void setLayerNr(unsigned int layer_nr);
|
||||
|
||||
void setOutputStream(std::ostream* stream);
|
||||
|
||||
|
||||
int getNozzleSize(int extruder_idx);
|
||||
|
||||
Point getExtruderOffset(int id);
|
||||
|
||||
Point getGcodePos(int64_t x, int64_t y, int extruder_train);
|
||||
@@ -190,25 +276,23 @@ 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();
|
||||
@@ -217,11 +301,17 @@ public:
|
||||
|
||||
void writeComment(std::string comment);
|
||||
void writeTypeComment(const char* type);
|
||||
void writeTypeComment(PrintFeatureType 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);
|
||||
@@ -231,6 +321,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);
|
||||
|
||||
@@ -252,20 +346,32 @@ public:
|
||||
ExtruderTrain* train = settings->getExtruderTrain(n);
|
||||
setFilamentDiameter(n, train->getSettingInMicrons("material_diameter"));
|
||||
|
||||
extruder_attr[n].nozzle_size = train->getSettingInMicrons("machine_nozzle_size");
|
||||
extruder_attr[n].nozzle_offset = Point(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
|
||||
|
||||
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");
|
||||
|
||||
extruder_attr[n].last_retraction_prime_speed = train->getSettingInMillimetersPerSecond("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured...
|
||||
}
|
||||
|
||||
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
|
||||
use_extruder_offset_to_offset_coords = settings->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
|
||||
|
||||
if (flavor == EGCodeFlavor::BFB)
|
||||
{
|
||||
new_line = "\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
new_line = "\n";
|
||||
}
|
||||
}
|
||||
void finalize(int maxObjectHeight, double moveSpeed, const char* endCode);
|
||||
void finalize(double moveSpeed, const char* endCode);
|
||||
|
||||
};
|
||||
|
||||
|
||||
+552
-265
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+387
-68
@@ -9,6 +9,8 @@
|
||||
#include "utils/logoutput.h"
|
||||
#include "wallOverlap.h"
|
||||
#include "commandSocket.h"
|
||||
#include "FanSpeedLayerTime.h"
|
||||
#include "SpaceFillType.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
@@ -16,48 +18,261 @@ 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.
|
||||
SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part
|
||||
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.
|
||||
|
||||
bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and end in one layer higher.
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the actual line width (modulated by the flow)
|
||||
* \return the actual line width as shown in layer view
|
||||
*/
|
||||
int getLineWidth()
|
||||
{
|
||||
return flow * config->getLineWidth() * config->getFlowPercentage() / 100.0;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
int layer_nr;
|
||||
|
||||
int z;
|
||||
|
||||
Point lastPosition;
|
||||
std::vector<GCodePath> paths;
|
||||
int layer_thickness;
|
||||
|
||||
bool was_combing;
|
||||
bool is_going_to_comb;
|
||||
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;
|
||||
@@ -68,10 +283,12 @@ private:
|
||||
* If GCodePlanner::forceNewPathStart has been called a new path will always be returned.
|
||||
*
|
||||
* \param config The config used for the path returned
|
||||
* \param space_fill_type The type of space filling which this path employs
|
||||
* \param flow (optional) A ratio for the extrusion speed
|
||||
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
|
||||
* \return A path with the given config which is now the last path in GCodePlanner::paths
|
||||
*/
|
||||
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, float flow = 1.0);
|
||||
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
|
||||
|
||||
/*!
|
||||
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
|
||||
@@ -85,26 +302,69 @@ 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(CommandSocket* commandSocket, GCodeExport& gcode, SliceDataStorage& storage, int z, 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(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;
|
||||
}
|
||||
|
||||
/*!
|
||||
* send a polygon through the command socket from the previous point to the given point
|
||||
*/
|
||||
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
|
||||
{
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
// we should send this travel as a non-retraction move
|
||||
cura::Polygons pathPoly;
|
||||
PolygonRef path = pathPoly.newPoly();
|
||||
path.add(from);
|
||||
path.add(to);
|
||||
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* 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()
|
||||
@@ -120,16 +380,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.
|
||||
@@ -147,75 +403,138 @@ public:
|
||||
* \param path (optional) The travel path to which to add the point \p p
|
||||
*/
|
||||
void addTravel_simple(Point p, GCodePath* path = nullptr);
|
||||
|
||||
void addExtrusionMove(Point p, GCodePathConfig* config, float flow = 1.0);
|
||||
|
||||
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr);
|
||||
/*!
|
||||
* Add an extrusion move to a certain point, optionally with a different flow than the one in the \p config.
|
||||
*
|
||||
* \param p The point to extrude to
|
||||
* \param config The config with which to extrude
|
||||
* \param space_fill_type Of what space filling type this extrusion move is a part
|
||||
* \param flow A modifier of the extrusion width which would follow from the \p config
|
||||
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
|
||||
*/
|
||||
void addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
|
||||
|
||||
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST);
|
||||
/*!
|
||||
* Add polygon to the gcode starting at vertex \p startIdx
|
||||
* \param polygon The polygon
|
||||
* \param startIdx The index of the starting vertex of the \p polygon
|
||||
* \param config The config with which to print the polygon lines
|
||||
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
|
||||
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over this polygon
|
||||
*/
|
||||
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, bool spiralize = false);
|
||||
|
||||
/*!
|
||||
* Add polygons to the gcode with optimized order.
|
||||
*
|
||||
* When \p spiralize is true, each polygon will gradually increase from a z corresponding to this layer to the z corresponding to the next layer.
|
||||
* Doing this for each polygon means there is a chance for the print head to crash into already printed parts,
|
||||
* but doing it for the last polygon only would mean you are printing half of the layer in non-spiralize mode,
|
||||
* while each layer starts with a different part.
|
||||
* Two towers would result in alternating spiralize and non-spiralize layers.
|
||||
*
|
||||
* \param polygons The polygons
|
||||
* \param config The config with which to print the polygon lines
|
||||
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
|
||||
* \param z_seam_type The seam type / poly start optimizer
|
||||
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over each polygon printed
|
||||
*/
|
||||
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, bool spiralize = false);
|
||||
|
||||
/*!
|
||||
* Add lines to the gcode with optimized order.
|
||||
* \param polygons The lines
|
||||
* \param config The config of the lines
|
||||
* \param space_fill_type The type of space filling used to generate the line segments (should be either Lines or PolyLines!)
|
||||
* \param wipe_dist (optional) the distance wiped without extruding after laying down a line.
|
||||
*/
|
||||
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist = 0);
|
||||
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, 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);
|
||||
|
||||
/*!
|
||||
* Writes a path to GCode and performs coasting, or returns false if it did nothing.
|
||||
* Write the planned paths to gcode
|
||||
*
|
||||
* Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines.
|
||||
*
|
||||
* \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.
|
||||
* \param coasting_speed_move The speed at which to move during move-coasting.
|
||||
* \param coasting_min_volume_move The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_move.
|
||||
* \param coasting_volume_retract The volume otherwise leaked during a retract move.
|
||||
* \param coasting_speed_retract The speed at which to move during retract-coasting.
|
||||
* \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.
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
*
|
||||
* Paths shorter than \p coasting_min_volume will use less \p coasting_volume linearly.
|
||||
*
|
||||
* \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.
|
||||
* \param coasting_volume The volume otherwise leaked.
|
||||
* \param coasting_speed The speed at which to move during coasting.
|
||||
* \param coasting_min_volume The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume.
|
||||
* \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);
|
||||
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 The volume otherwise leaked during a normal move.
|
||||
* \param coasting_speed The speed at which to move during move-coasting.
|
||||
* \param coasting_min_volume The minimal volume a path should have (before starting to coast) which builds up enough pressure to ooze as much as \p coasting_volume.
|
||||
* \return Whether any GCode has been written for the path.
|
||||
*/
|
||||
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume);
|
||||
|
||||
/*!
|
||||
* 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
|
||||
|
||||
+158
-393
@@ -2,7 +2,6 @@
|
||||
#include "infill.h"
|
||||
#include "functional"
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "utils/AABB.h"
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura {
|
||||
@@ -16,36 +15,28 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
|
||||
switch(pattern)
|
||||
{
|
||||
case EFillMethod::GRID:
|
||||
generateGridInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 2, infill_overlap, fill_angle);
|
||||
generateGridInfill(result_lines);
|
||||
break;
|
||||
case EFillMethod::LINES:
|
||||
generateLineInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle);
|
||||
generateLineInfill(result_lines, line_distance, fill_angle);
|
||||
break;
|
||||
case EFillMethod::TRIANGLES:
|
||||
generateTriangleInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 3, infill_overlap, fill_angle);
|
||||
generateTriangleInfill(result_lines);
|
||||
break;
|
||||
case EFillMethod::CONCENTRIC:
|
||||
if (outlineOffset != 0)
|
||||
PolygonUtils::offsetSafe(in_outline, outline_offset - infill_line_width / 2, infill_line_width, outline_offsetted, false); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
|
||||
outline = &outline_offsetted;
|
||||
if (abs(infill_line_width - line_distance) < 10)
|
||||
{
|
||||
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
|
||||
outline = &outline_offsetted;
|
||||
generateConcentricInfillDense(*outline, result_polygons, in_between, remove_overlapping_perimeters);
|
||||
}
|
||||
if (abs(extrusion_width - line_distance) < 10)
|
||||
{
|
||||
generateConcentricInfillDense(*outline, result_polygons, in_between, extrusion_width, avoidOverlappingPerimeters);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
generateConcentricInfill(*outline, result_polygons, line_distance);
|
||||
}
|
||||
break;
|
||||
case EFillMethod::ZIG_ZAG:
|
||||
if (outlineOffset != 0)
|
||||
{
|
||||
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
|
||||
outline = &outline_offsetted;
|
||||
}
|
||||
generateZigZagInfill(*outline, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle, connect_zigzags, use_endPieces);
|
||||
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
|
||||
break;
|
||||
default:
|
||||
logError("Fill pattern has unknown value.\n");
|
||||
@@ -53,9 +44,9 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters)
|
||||
|
||||
|
||||
void Infill::generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
while(outline.size() > 0)
|
||||
{
|
||||
@@ -65,13 +56,13 @@ void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons*
|
||||
result.add(r);
|
||||
}
|
||||
Polygons next_outline;
|
||||
PolygonUtils::offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
|
||||
PolygonUtils::offsetExtrusionWidth(outline, true, infill_line_width, next_outline, in_between, avoidOverlappingPerimeters);
|
||||
outline = next_outline;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
|
||||
void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
|
||||
{
|
||||
while(outline.size() > 0)
|
||||
{
|
||||
@@ -85,152 +76,95 @@ void generateConcentricInfill(Polygons outline, Polygons& result, int inset_valu
|
||||
}
|
||||
|
||||
|
||||
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
|
||||
int extrusionWidth, int lineSpacing, double infillOverlap,
|
||||
double rotation)
|
||||
void Infill::generateGridInfill(Polygons& result)
|
||||
{
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation + 90);
|
||||
generateLineInfill(result, line_distance * 2, fill_angle);
|
||||
generateLineInfill(result, line_distance * 2, fill_angle + 90);
|
||||
}
|
||||
|
||||
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
|
||||
int extrusionWidth, int lineSpacing, double infillOverlap,
|
||||
double rotation)
|
||||
void Infill::generateTriangleInfill(Polygons& result)
|
||||
{
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation + 60);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation + 120);
|
||||
generateLineInfill(result, line_distance * 3, fill_angle);
|
||||
generateLineInfill(result, line_distance * 3, fill_angle + 60);
|
||||
generateLineInfill(result, line_distance * 3, fill_angle + 120);
|
||||
}
|
||||
|
||||
void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, int lineSpacing, AABB boundary, std::vector<std::vector<int64_t> > cutList, int extrusionWidth)
|
||||
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list)
|
||||
{
|
||||
auto addLine = [&](Point from, Point to)
|
||||
{
|
||||
{
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(matrix.unapply(from));
|
||||
p.add(matrix.unapply(to));
|
||||
p.add(rotation_matrix.unapply(from));
|
||||
p.add(rotation_matrix.unapply(to));
|
||||
};
|
||||
|
||||
|
||||
auto compare_int64_t = [](const void* a, const void* b)
|
||||
{
|
||||
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
|
||||
if (n < 0) return -1;
|
||||
if (n > 0) return 1;
|
||||
if (n < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (n > 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
||||
int scanline_idx = 0;
|
||||
for(int64_t x = scanline_min_idx * lineSpacing; x < boundary.max.X; x += lineSpacing)
|
||||
for(int64_t x = scanline_min_idx * line_distance; x < boundary.max.X; x += line_distance)
|
||||
{
|
||||
qsort(cutList[scanline_idx].data(), cutList[scanline_idx].size(), sizeof(int64_t), compare_int64_t);
|
||||
for(unsigned int i = 0; i + 1 < cutList[scanline_idx].size(); i+=2)
|
||||
std::vector<int64_t>& crossings = cut_list[scanline_idx];
|
||||
qsort(crossings.data(), crossings.size(), sizeof(int64_t), compare_int64_t);
|
||||
for(unsigned int crossing_idx = 0; crossing_idx + 1 < crossings.size(); crossing_idx += 2)
|
||||
{
|
||||
if (cutList[scanline_idx][i+1] - cutList[scanline_idx][i] < extrusionWidth / 5)
|
||||
if (crossings[crossing_idx + 1] - crossings[crossing_idx] < infill_line_width / 5)
|
||||
{ // segment is too short to create infill
|
||||
continue;
|
||||
addLine(Point(x, cutList[scanline_idx][i]), Point(x, cutList[scanline_idx][i+1]));
|
||||
}
|
||||
addLine(Point(x, crossings[crossing_idx]), Point(x, crossings[crossing_idx + 1]));
|
||||
}
|
||||
scanline_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* 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 Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle)
|
||||
{
|
||||
if (lineSpacing == 0) return;
|
||||
if (in_outline.size() == 0) return;
|
||||
Polygons outline = ((outlineOffset)? in_outline.offset(outlineOffset) : in_outline).offset(extrusionWidth * infillOverlap / 100);
|
||||
if (outline.size() == 0) return;
|
||||
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
outline.applyMatrix(matrix);
|
||||
PointMatrix rotation_matrix(fill_angle);
|
||||
NoZigZagConnectorProcessor lines_processor(rotation_matrix, result);
|
||||
bool connected_zigzags = false;
|
||||
bool safe_outline_offset = false;
|
||||
generateLinearBasedInfill(outline_offset, safe_outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags);
|
||||
}
|
||||
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
int scanline_min_idx = boundary.min.X / lineSpacing;
|
||||
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int n=0; n<lineCount; n++)
|
||||
cutList.push_back(std::vector<int64_t>());
|
||||
|
||||
for(unsigned int poly_idx=0; poly_idx < outline.size(); poly_idx++)
|
||||
|
||||
void Infill::generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces)
|
||||
{
|
||||
bool safe_outline_offset = true;
|
||||
|
||||
PointMatrix rotation_matrix(fill_angle);
|
||||
if (use_endpieces)
|
||||
{
|
||||
Point p0 = outline[poly_idx][outline[poly_idx].size()-1];
|
||||
for(unsigned int i=0; i < outline[poly_idx].size(); i++)
|
||||
if (connected_zigzags)
|
||||
{
|
||||
Point p1 = outline[poly_idx][i];
|
||||
int64_t xMin = p1.X, xMax = p0.X;
|
||||
if (xMin == xMax) {
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
int direction = 1;
|
||||
if (p0.X > p1.X)
|
||||
{
|
||||
direction = -1;
|
||||
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
|
||||
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
|
||||
{
|
||||
int x = scanline_idx * lineSpacing;
|
||||
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
|
||||
cutList[scanline_idx - scanline_min_idx].push_back(y);
|
||||
}
|
||||
p0 = p1;
|
||||
ZigzagConnectorProcessorConnectedEndPieces zigzag_processor(rotation_matrix, result);
|
||||
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
|
||||
}
|
||||
else
|
||||
{
|
||||
ZigzagConnectorProcessorDisconnectedEndPieces zigzag_processor(rotation_matrix, result);
|
||||
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
|
||||
}
|
||||
}
|
||||
|
||||
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
|
||||
else
|
||||
{
|
||||
ZigzagConnectorProcessorNoEndPieces zigzag_processor(rotation_matrix, result);
|
||||
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces)
|
||||
{
|
||||
if (use_endPieces) return generateZigZagIninfill_endPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation, connect_zigzags);
|
||||
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
|
||||
@@ -239,288 +173,119 @@ void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extr
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
* zigzag algorithm:
|
||||
* rough explanation of the 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])
|
||||
* (see infill/ZigzagConnectorProcessor.h for actual implementation details)
|
||||
*
|
||||
*
|
||||
* <--
|
||||
* ___
|
||||
* | | |
|
||||
* | | |
|
||||
* | |___|
|
||||
* -->
|
||||
*
|
||||
* ^ = even scanline
|
||||
*
|
||||
* start boundary from even scanline! :D
|
||||
*
|
||||
*
|
||||
* _____
|
||||
* | | | ,
|
||||
* | | | |
|
||||
* |_____| |__/
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
* ^ disconnected end piece
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'.
|
||||
* Scansegment x is the area between scanline x and scanline x+1
|
||||
* Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment,
|
||||
* while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
|
||||
*/
|
||||
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags)
|
||||
void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags)
|
||||
{
|
||||
// if (in_outline.size() == 0) return;
|
||||
// Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
|
||||
Polygons empty;
|
||||
Polygons outline = in_outline.difference(empty); // copy
|
||||
if (outline.size() == 0) return;
|
||||
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
outline.applyMatrix(matrix);
|
||||
|
||||
auto addLine = [&](Point from, Point to)
|
||||
{
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(matrix.unapply(from));
|
||||
p.add(matrix.unapply(to));
|
||||
};
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
int scanline_min_idx = boundary.min.X / lineSpacing;
|
||||
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int n=0; n<lineCount; n++)
|
||||
cutList.push_back(std::vector<int64_t>());
|
||||
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
|
||||
if (line_distance == 0)
|
||||
{
|
||||
std::vector<Point> firstBoundarySegment;
|
||||
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 isEvenScanSegment = false;
|
||||
|
||||
|
||||
Point p0 = outline[polyNr][outline[polyNr].size()-1];
|
||||
Point lastPoint = p0;
|
||||
for(unsigned int i=0; i < outline[polyNr].size(); i++)
|
||||
return;
|
||||
}
|
||||
if (in_outline.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Polygons outline;
|
||||
if (outline_offset != 0)
|
||||
{
|
||||
PolygonUtils::offsetSafe(in_outline, outline_offset, infill_line_width, outline, remove_overlapping_perimeters && safe_outline_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
outline = in_outline;
|
||||
}
|
||||
|
||||
outline = outline.offset(infill_overlap * infill_line_width / 100); // division by 100 cause it's a percentage.
|
||||
|
||||
if (outline.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
outline.applyMatrix(rotation_matrix);
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
int scanline_min_idx = boundary.min.X / line_distance;
|
||||
int line_count = (boundary.max.X + (line_distance - 1)) / line_distance - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++)
|
||||
{
|
||||
cut_list.push_back(std::vector<int64_t>());
|
||||
}
|
||||
|
||||
for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++)
|
||||
{
|
||||
PolygonRef poly = outline[poly_idx];
|
||||
Point p0 = poly.back();
|
||||
zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type
|
||||
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
Point p1 = outline[polyNr][i];
|
||||
int64_t xMin = p1.X, xMax = p0.X;
|
||||
if (xMin == xMax) {
|
||||
lastPoint = p1;
|
||||
Point p1 = poly[point_idx];
|
||||
if (p1.X == p0.X)
|
||||
{
|
||||
zigzag_connector_processor.registerVertex(p1);
|
||||
// TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors)
|
||||
// note: this is already a problem for normal infill, but hasn't really cothered anyone so far.
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -line_distance)) / line_distance; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -line_distance)) / line_distance; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
// this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
|
||||
// in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
|
||||
// otherwise only 1 will be created, counting as an actual intersection
|
||||
int direction = 1;
|
||||
if (p0.X > p1.X)
|
||||
{
|
||||
direction = -1;
|
||||
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
|
||||
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
|
||||
|
||||
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
|
||||
}
|
||||
else
|
||||
{
|
||||
int x = scanline_idx * lineSpacing;
|
||||
scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
}
|
||||
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
|
||||
{
|
||||
int x = scanline_idx * line_distance;
|
||||
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
|
||||
cutList[scanline_idx - scanline_min_idx].push_back(y);
|
||||
|
||||
|
||||
bool last_isEvenScanSegment = isEvenScanSegment;
|
||||
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
|
||||
else isEvenScanSegment = false;
|
||||
|
||||
if (!isFirstBoundarySegment)
|
||||
{
|
||||
if (last_isEvenScanSegment && (connect_zigzags || !isEvenScanSegment))
|
||||
addLine(lastPoint, Point(x,y));
|
||||
else if (connect_zigzags && !last_isEvenScanSegment && !isEvenScanSegment) // if we end an uneven boundary in an uneven segment
|
||||
{ // add whole unevenBoundarySegment (including the just obtained point)
|
||||
for (unsigned int p = 1; p < unevenBoundarySegment.size(); p++)
|
||||
{
|
||||
addLine(unevenBoundarySegment[p-1], unevenBoundarySegment[p]);
|
||||
}
|
||||
addLine(unevenBoundarySegment[unevenBoundarySegment.size()-1], Point(x,y));
|
||||
unevenBoundarySegment.clear();
|
||||
}
|
||||
if (connect_zigzags && last_isEvenScanSegment && !isEvenScanSegment)
|
||||
unevenBoundarySegment.push_back(Point(x,y));
|
||||
else
|
||||
unevenBoundarySegment.clear();
|
||||
|
||||
}
|
||||
lastPoint = Point(x,y);
|
||||
|
||||
if (isFirstBoundarySegment)
|
||||
{
|
||||
firstBoundarySegment.emplace_back(x,y);
|
||||
firstBoundarySegmentEndsInEven = isEvenScanSegment;
|
||||
isFirstBoundarySegment = false;
|
||||
}
|
||||
|
||||
cut_list[scanline_idx - scanline_min_idx].push_back(y);
|
||||
Point scanline_linesegment_intersection(x, y);
|
||||
zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
|
||||
}
|
||||
if (!isFirstBoundarySegment)
|
||||
{
|
||||
if (isEvenScanSegment)
|
||||
addLine(lastPoint, p1);
|
||||
else if (connect_zigzags)
|
||||
unevenBoundarySegment.push_back(p1);
|
||||
}
|
||||
|
||||
lastPoint = p1;
|
||||
zigzag_connector_processor.registerVertex(p1);
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
if (isEvenScanSegment || isFirstBoundarySegment || connect_zigzags)
|
||||
{
|
||||
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
|
||||
{
|
||||
if (i < firstBoundarySegment.size() - 1 || !firstBoundarySegmentEndsInEven || connect_zigzags) // only add last element if connect_zigzags or boundary segment ends in uneven scanline
|
||||
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
|
||||
}
|
||||
}
|
||||
else if (!firstBoundarySegmentEndsInEven)
|
||||
addLine(firstBoundarySegment[firstBoundarySegment.size()-2], firstBoundarySegment[firstBoundarySegment.size()-1]);
|
||||
}
|
||||
|
||||
if (cutList.size() == 0) return;
|
||||
if (connect_zigzags && cutList.size() == 1 && cutList[0].size() <= 2) return; // don't add connection if boundary already contains whole outline!
|
||||
|
||||
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
|
||||
}
|
||||
zigzag_connector_processor.registerPolyFinished();
|
||||
}
|
||||
|
||||
|
||||
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
|
||||
{
|
||||
if (in_outline.size() == 0) return;
|
||||
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
|
||||
if (outline.size() == 0) return;
|
||||
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
outline.applyMatrix(matrix);
|
||||
|
||||
auto addLine = [&](Point from, Point to)
|
||||
{
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(matrix.unapply(from));
|
||||
p.add(matrix.unapply(to));
|
||||
};
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
int scanline_min_idx = boundary.min.X / lineSpacing;
|
||||
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int n=0; n<lineCount; n++)
|
||||
cutList.push_back(std::vector<int64_t>());
|
||||
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
|
||||
if (cut_list.size() == 0)
|
||||
{
|
||||
std::vector<Point> firstBoundarySegment;
|
||||
std::vector<Point> boundarySegment;
|
||||
|
||||
bool isFirstBoundarySegment = true;
|
||||
bool firstBoundarySegmentEndsInEven;
|
||||
|
||||
bool isEvenScanSegment = false;
|
||||
|
||||
|
||||
Point p0 = outline[polyNr][outline[polyNr].size()-1];
|
||||
for(unsigned int i=0; i < outline[polyNr].size(); i++)
|
||||
{
|
||||
Point p1 = outline[polyNr][i];
|
||||
int64_t xMin = p1.X, xMax = p0.X;
|
||||
if (xMin == xMax) {
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
int direction = 1;
|
||||
if (p0.X > p1.X)
|
||||
{
|
||||
direction = -1;
|
||||
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
|
||||
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
|
||||
|
||||
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
|
||||
else boundarySegment.push_back(p0);
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
|
||||
{
|
||||
int x = scanline_idx * lineSpacing;
|
||||
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
|
||||
cutList[scanline_idx - scanline_min_idx].push_back(y);
|
||||
|
||||
|
||||
bool last_isEvenScanSegment = isEvenScanSegment;
|
||||
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
|
||||
else isEvenScanSegment = false;
|
||||
|
||||
if (!isFirstBoundarySegment)
|
||||
{
|
||||
if (last_isEvenScanSegment && !isEvenScanSegment)
|
||||
{ // add whole boundarySegment (including the just obtained point)
|
||||
for (unsigned int p = 1; p < boundarySegment.size(); p++)
|
||||
{
|
||||
addLine(boundarySegment[p-1], boundarySegment[p]);
|
||||
}
|
||||
addLine(boundarySegment[boundarySegment.size()-1], Point(x,y));
|
||||
boundarySegment.clear();
|
||||
}
|
||||
else if (isEvenScanSegment) // we are either in an end piece or an uneven boundary segment
|
||||
{
|
||||
boundarySegment.clear();
|
||||
boundarySegment.emplace_back(x,y);
|
||||
} else
|
||||
boundarySegment.clear();
|
||||
|
||||
}
|
||||
|
||||
if (isFirstBoundarySegment)
|
||||
{
|
||||
firstBoundarySegment.emplace_back(x,y);
|
||||
firstBoundarySegmentEndsInEven = isEvenScanSegment;
|
||||
isFirstBoundarySegment = false;
|
||||
boundarySegment.emplace_back(x,y);
|
||||
}
|
||||
|
||||
}
|
||||
if (!isFirstBoundarySegment && isEvenScanSegment)
|
||||
boundarySegment.push_back(p1);
|
||||
|
||||
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
if (!isFirstBoundarySegment && isEvenScanSegment && !firstBoundarySegmentEndsInEven)
|
||||
{
|
||||
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
|
||||
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
|
||||
return;
|
||||
}
|
||||
if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2)
|
||||
{
|
||||
return; // don't add connection if boundary already contains whole outline!
|
||||
}
|
||||
|
||||
addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list);
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+160
-27
@@ -4,48 +4,181 @@
|
||||
|
||||
#include "utils/polygon.h"
|
||||
#include "settings.h"
|
||||
// #include "ZigzagConnectorProcessor.h"
|
||||
#include "infill/ZigzagConnectorProcessor.h"
|
||||
#include "infill/NoZigZagConnectorProcessor.h"
|
||||
#include "infill/ActualZigzagConnectorProcessor.h"
|
||||
#include "infill/ZigzagConnectorProcessorNoEndPieces.h"
|
||||
#include "infill/ZigzagConnectorProcessorEndPieces.h"
|
||||
#include "infill/ZigzagConnectorProcessorConnectedEndPieces.h"
|
||||
#include "infill/ZigzagConnectorProcessorDisconnectedEndPieces.h"
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/AABB.h"
|
||||
|
||||
namespace cura {
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class 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;
|
||||
EFillMethod pattern; //!< the space filling pattern of the infill to generate
|
||||
const Polygons& in_outline; //!< a reference polygon for getting the actual area within which to generate infill (see outline_offset)
|
||||
int outline_offset; //!< Offset from Infill::in_outline to get the actual area within which to generate infill
|
||||
bool remove_overlapping_perimeters; //!< Whether to remove overlapping perimeter parts
|
||||
int infill_line_width; //!< The line width of the infill lines to generate
|
||||
int line_distance; //!< The distance between two infill lines / polygons
|
||||
double infill_overlap; //!< the percentage (of infill_line_width) to overlap with the actual area within which to generate infill
|
||||
double fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid)
|
||||
bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector
|
||||
bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself
|
||||
|
||||
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)
|
||||
Infill(EFillMethod pattern, const Polygons& in_outline, int outline_offset, bool remove_overlapping_perimeters, int infill_line_width, int line_distance, double infill_overlap, double fill_angle, bool connected_zigzags = false, bool use_endpieces = false)
|
||||
: pattern(pattern)
|
||||
, in_outline(in_outline)
|
||||
, outlineOffset(outlineOffset)
|
||||
, avoidOverlappingPerimeters(avoidOverlappingPerimeters)
|
||||
, extrusion_width(extrusion_width)
|
||||
, outline_offset(outline_offset)
|
||||
, remove_overlapping_perimeters(remove_overlapping_perimeters)
|
||||
, infill_line_width(infill_line_width)
|
||||
, line_distance(line_distance)
|
||||
, infill_overlap(infill_overlap)
|
||||
, fill_angle(fill_angle)
|
||||
, connect_zigzags(connect_zigzags)
|
||||
, use_endPieces(use_endPieces)
|
||||
, connected_zigzags(connected_zigzags)
|
||||
, use_endpieces(use_endpieces)
|
||||
{
|
||||
}
|
||||
/*!
|
||||
* Generate the infill.
|
||||
*
|
||||
* \param result_polygons (output) The resulting polygons (from concentric infill)
|
||||
* \param result_lines (output) The resulting line segments (from linear infill types)
|
||||
* \param in_between (optional output) The areas in between two concecutive concentric infill polygons
|
||||
*/
|
||||
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
/*!
|
||||
* Generate sparse concentric infill
|
||||
* \param outline The actual outline of the area within which to generate infill
|
||||
* \param result (output) The resulting polygons
|
||||
* \param inset_value The offset between each consecutive two polygons
|
||||
*/
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
|
||||
|
||||
/*!
|
||||
* Generate dense concentric infill (100%)
|
||||
*
|
||||
* \param outline The actual outline of the area within which to generate infill
|
||||
* \param result (output) The resulting polygons
|
||||
* \param in_between (output) The areas in between each two consecutive polygons
|
||||
* \param remove_overlapping_perimeters Whether to remove overlapping perimeter parts
|
||||
*/
|
||||
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool remove_overlapping_perimeters);
|
||||
|
||||
/*!
|
||||
* Generate a rectangular grid of infill lines
|
||||
* \param result (output) The resulting lines
|
||||
*/
|
||||
void generateGridInfill(Polygons& result);
|
||||
|
||||
/*!
|
||||
* Generate a triangular grid of infill lines
|
||||
* \param result (output) The resulting lines
|
||||
*/
|
||||
void generateTriangleInfill(Polygons& result);
|
||||
|
||||
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);
|
||||
/*!
|
||||
* Convert a mapping from scanline to line_segment-scanline-intersections (\p cut_list) into line segments, using the even-odd rule
|
||||
* \param result (output) The resulting lines
|
||||
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
|
||||
* \param scanline_min_idx The lowest index of all scanlines crossing the polygon
|
||||
* \param line_distance The distance between two lines which are in the same direction
|
||||
* \param boundary The axis aligned boundary box within which the polygon is
|
||||
* \param cut_list A mapping of each scanline to all y-coordinates (in the space transformed by rotation_matrix) where the polygons are crossing the scanline
|
||||
*/
|
||||
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list);
|
||||
|
||||
/*!
|
||||
* generate lines within the area of \p in_outline, at regular intervals of \p line_distance
|
||||
*
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
|
||||
*
|
||||
* \param result (output) The resulting lines
|
||||
* \param line_distance The distance between two lines which are in the same direction
|
||||
* \param fill_angle The angle of the generated lines
|
||||
*/
|
||||
void generateLineInfill(Polygons& result, int line_distance, const double& fill_angle);
|
||||
|
||||
/*!
|
||||
* Function for creating linear based infill types (Lines, ZigZag).
|
||||
*
|
||||
* This function implements the basic functionality of Infill::generateLineInfill (see doc of that function),
|
||||
* but makes calls to a ZigzagConnectorProcessor which handles what to do with each line segment - scanline intersection.
|
||||
*
|
||||
* It is called only from Infill::generateLineinfill and Infill::generateZigZagInfill.
|
||||
*
|
||||
* \param outline_offset An offset from the reference polygon (Infill::in_outline) to get the actual outline within which to generate infill
|
||||
* \param safe_outline_offset Whether to consider removing overlapping wall parts (not so for normal line infill)
|
||||
* \param result (output) The resulting lines
|
||||
* \param line_distance The distance between two lines which are in the same direction
|
||||
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
|
||||
* \param zigzag_connector_processor The processor used to generate zigzag connectors
|
||||
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
|
||||
*/
|
||||
void generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags);
|
||||
|
||||
/*!
|
||||
*
|
||||
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
|
||||
* zigzag:
|
||||
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
|
||||
*
|
||||
* Note that ZigZag consists of 3 types:
|
||||
* - without endpieces
|
||||
* - with disconnected endpieces
|
||||
* - with connected endpieces
|
||||
*
|
||||
* <--
|
||||
* ___
|
||||
* | | |
|
||||
* | | |
|
||||
* | |___|
|
||||
* -->
|
||||
*
|
||||
* ^ = even scanline
|
||||
* ^ ^ no endpieces
|
||||
*
|
||||
* start boundary from even scanline! :D
|
||||
*
|
||||
*
|
||||
* v disconnected end piece: leave out last line segment
|
||||
* _____
|
||||
* | | | \ .
|
||||
* | | | |
|
||||
* |_____| |__/
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
*
|
||||
*
|
||||
* v connected end piece
|
||||
* ________
|
||||
* | | | \ .
|
||||
* | | | |
|
||||
* |_____| |__/ .
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
*
|
||||
* \param result (output) The resulting lines
|
||||
* \param line_distance The distance between two lines which are in the same direction
|
||||
* \param fill_angle The angle of the generated lines
|
||||
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
|
||||
* \param use_endpieces Whether to include zigzag segments connecting a scanline to itself
|
||||
*/
|
||||
void generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//INFILL_H
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
#define INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ZigzagConnectorProcessor.h"
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* In contrast to NoZigZagConnectorProcessor
|
||||
*/
|
||||
class ActualZigzagConnectorProcessor : public ZigzagConnectorProcessor
|
||||
{
|
||||
protected:
|
||||
/*!
|
||||
* The line segments belonging the zigzag connector to which the very first vertex belongs.
|
||||
* This will be combined with the last handled zigzag_connector, which combine to a whole zigzag connector.
|
||||
*
|
||||
* Because the boundary polygon may start in in the middle of a zigzag connector,
|
||||
*/
|
||||
std::vector<Point> first_zigzag_connector;
|
||||
/*!
|
||||
* The currently built up zigzag connector (not the first/last) or end piece or discarded boundary segment
|
||||
*/
|
||||
std::vector<Point> zigzag_connector;
|
||||
|
||||
bool is_first_zigzag_connector; //!< Whether we're still in the first zigzag connector
|
||||
bool first_zigzag_connector_ends_in_even_scanline; //!< Whether the first zigzag connector ends in an even scanline
|
||||
bool last_scanline_is_even; //!< Whether the last seen scanline-boundary intersection was with an even scanline
|
||||
|
||||
ActualZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ZigzagConnectorProcessor(rotation_matrix, result)
|
||||
, is_first_zigzag_connector(true)
|
||||
, first_zigzag_connector_ends_in_even_scanline(true)
|
||||
, last_scanline_is_even(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
@@ -0,0 +1,25 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "NoZigZagConnectorProcessor.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void NoZigZagConnectorProcessor::registerVertex(const Point& vertex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NoZigZagConnectorProcessor::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NoZigZagConnectorProcessor::registerPolyFinished()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,28 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
#define INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ZigzagConnectorProcessor.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class NoZigZagConnectorProcessor : public ZigzagConnectorProcessor
|
||||
{
|
||||
public:
|
||||
NoZigZagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ZigzagConnectorProcessor(rotation_matrix, result)
|
||||
{
|
||||
}
|
||||
|
||||
void registerVertex(const Point& vertex);
|
||||
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
|
||||
void registerPolyFinished();
|
||||
};
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
@@ -0,0 +1,154 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Processor class for processing the connections between lines which makes the infill a zigzag pattern.
|
||||
*
|
||||
* During the creation of the infill lines, calls are made to a ZigzagConnectorProcessor so that the zigzag connector segments are created
|
||||
* at the same time as the lines are created.
|
||||
*
|
||||
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
|
||||
* zigzag:
|
||||
* 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 [connected_zigzags])
|
||||
*
|
||||
* Note that ZigZag consists of 3 types:
|
||||
* - without endpieces
|
||||
* - with disconnected endpieces
|
||||
* - with connected endpieces
|
||||
*
|
||||
* Each of these has a base class for which ZigzagConnectorProcessor is an ancestor.
|
||||
* The inheritance structure is as such:
|
||||
* ZigzagConnectorProcessor
|
||||
* / \ .
|
||||
* / \ .
|
||||
* ActualZigzagConnectorProcessor NoZigZagConnectorProcessor
|
||||
* / \ for lines infill .
|
||||
* / \ .
|
||||
* ZigzagConnectorProcessorEndPieces ZigzagConnectorProcessorNoEndPieces
|
||||
* / \ for zigzag infill (without end pieces) .
|
||||
* / \ .
|
||||
* ZigzagConnectorProcessorConnectedEndPieces ZigzagConnectorProcessorDisconnectedEndPieces
|
||||
* for zigzag support with normal endpieces for zigzag support with disconnected endpieces for more easy removability
|
||||
*
|
||||
* v v zigzag connectors
|
||||
* <--
|
||||
* :___: : < scanlines
|
||||
* | | |
|
||||
* | | | < infill lines along scanlines
|
||||
* | |___|
|
||||
* : : :
|
||||
* --> winding order of polygon
|
||||
*
|
||||
* ^ = even scanline
|
||||
* ^ ^ no endpieces
|
||||
*
|
||||
* start boundary from even scanline! :D
|
||||
* include only a boundary segment if it starts in an even scanline and ends in an odd scanline
|
||||
*
|
||||
* ________
|
||||
* | | | \ .
|
||||
* | | | |
|
||||
* |_____| |__/ .
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
* ^ connected end piece
|
||||
* include a boundary segment also if it starts in an odd scanline and ends odd,
|
||||
* or starts in an even scanline and ends in an even scanline,
|
||||
* but not when it starts in an odd and ends in an even scanline (see top left or bottom middle).
|
||||
*
|
||||
* _____
|
||||
* | | | \ .
|
||||
* | | | |
|
||||
* |_____| |__/
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
* ^ disconnected end piece
|
||||
* Leave out the last line segment of the boundary polygon: from a vertex to the linesegment-scanline intersection.
|
||||
*/
|
||||
class ZigzagConnectorProcessor
|
||||
{
|
||||
protected:
|
||||
const PointMatrix& rotation_matrix; //!< The rotation matrix used to enforce the infill angle
|
||||
Polygons& result; //!< The result of the computation
|
||||
|
||||
virtual ~ZigzagConnectorProcessor()
|
||||
{}
|
||||
|
||||
/*!
|
||||
* Add a line to the result bu unapplying the rotation rotation_matrix.
|
||||
*
|
||||
* \param from The one end of the line segment
|
||||
* \param to The other end of the line segment
|
||||
*/
|
||||
void addLine(Point from, Point to)
|
||||
{
|
||||
PolygonRef line_poly = result.newPoly();
|
||||
line_poly.add(rotation_matrix.unapply(from));
|
||||
line_poly.add(rotation_matrix.unapply(to));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Basic constructor. Inheriting children should call this constructor.
|
||||
*
|
||||
* \param rotation_matrix The rotation matrix used to enforce the infill angle
|
||||
* \param result The resulting line segments (Each line segment is a Polygon with 2 points)
|
||||
*/
|
||||
ZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: rotation_matrix(rotation_matrix)
|
||||
, result(result)
|
||||
{}
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Handle the next vertex on the outer boundary.
|
||||
* \param vertex The vertex
|
||||
*/
|
||||
virtual void registerVertex(const Point& vertex) = 0;
|
||||
|
||||
/*!
|
||||
* Handle the next intersection between a scanline and the outer boundary.
|
||||
*
|
||||
* \param intersection The intersection
|
||||
* \param scanline_is_even Whether the scanline was even
|
||||
*/
|
||||
virtual void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even) = 0;
|
||||
|
||||
/*!
|
||||
* Handle the end of a polygon and prepare for the next.
|
||||
* This function should reset all member variables.
|
||||
*/
|
||||
virtual void registerPolyFinished() = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
@@ -0,0 +1,75 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "ZigzagConnectorProcessorConnectedEndPieces.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
void ZigzagConnectorProcessorConnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
|
||||
{
|
||||
bool previous_scanline_is_even = last_scanline_is_even;
|
||||
last_scanline_is_even = scanline_is_even;
|
||||
bool this_scanline_is_even = last_scanline_is_even;
|
||||
|
||||
if (is_first_zigzag_connector)
|
||||
{
|
||||
first_zigzag_connector.push_back(intersection);
|
||||
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
|
||||
is_first_zigzag_connector = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (previous_scanline_is_even)
|
||||
{ // when a boundary segment starts in an even scanline it is either a normal zigzag connector or an endpiece, so it should be included anyway
|
||||
addLine(last_connector_point, intersection);
|
||||
}
|
||||
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
|
||||
{ // add whole zigzag_connector (including the just obtained point)
|
||||
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
|
||||
}
|
||||
addLine(zigzag_connector.back(), intersection);
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
|
||||
}
|
||||
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
|
||||
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
|
||||
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
|
||||
zigzag_connector.push_back(intersection);
|
||||
}
|
||||
|
||||
last_connector_point = intersection;
|
||||
}
|
||||
|
||||
|
||||
void ZigzagConnectorProcessorConnectedEndPieces::registerPolyFinished()
|
||||
{
|
||||
// write end segment if needed (first half of start/end-crossing segment)
|
||||
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|
||||
{
|
||||
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
|
||||
}
|
||||
}
|
||||
// write begin segment if needed (second half of start/end-crossing segment)
|
||||
if (last_scanline_is_even || (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|
||||
|| is_first_zigzag_connector)
|
||||
{
|
||||
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
|
||||
}
|
||||
}
|
||||
// reset member variables
|
||||
is_first_zigzag_connector = true;
|
||||
first_zigzag_connector_ends_in_even_scanline = true;
|
||||
last_scanline_is_even = false;
|
||||
first_zigzag_connector.clear();
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,27 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
|
||||
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ZigzagConnectorProcessorEndPieces.h"
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
class ZigzagConnectorProcessorConnectedEndPieces : public ZigzagConnectorProcessorEndPieces
|
||||
{
|
||||
public:
|
||||
ZigzagConnectorProcessorConnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
|
||||
{
|
||||
}
|
||||
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
|
||||
void registerPolyFinished();
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
|
||||
@@ -0,0 +1,79 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "ZigzagConnectorProcessorDisconnectedEndPieces.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void ZigzagConnectorProcessorDisconnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
|
||||
{
|
||||
bool previous_scanline_is_even = last_scanline_is_even;
|
||||
last_scanline_is_even = scanline_is_even;
|
||||
bool this_scanline_is_even = last_scanline_is_even;
|
||||
|
||||
if (is_first_zigzag_connector)
|
||||
{
|
||||
first_zigzag_connector.push_back(intersection);
|
||||
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
|
||||
is_first_zigzag_connector = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (previous_scanline_is_even && !this_scanline_is_even)
|
||||
{ // if we left from an even scanline, but not if this is the line segment connecting that zigzag_connector to an even scanline
|
||||
addLine(last_connector_point, intersection);
|
||||
}
|
||||
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
|
||||
{ // add whole oddBoundarySegment (including the just obtained point)
|
||||
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
|
||||
}
|
||||
// skip the last segment to the [intersection]
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
|
||||
}
|
||||
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
|
||||
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
|
||||
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
|
||||
zigzag_connector.push_back(intersection);
|
||||
}
|
||||
|
||||
last_connector_point = intersection;
|
||||
}
|
||||
|
||||
|
||||
void ZigzagConnectorProcessorDisconnectedEndPieces::registerPolyFinished()
|
||||
{
|
||||
// write end segment if needed (first half of start/end-crossing segment)
|
||||
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|
||||
{
|
||||
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
|
||||
}
|
||||
}
|
||||
// write begin segment if needed (second half of start/end-crossing segment)
|
||||
if (last_scanline_is_even || is_first_zigzag_connector)
|
||||
{
|
||||
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() - 1; point_idx++) // -1 cause skipping very last line segment!
|
||||
{
|
||||
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
|
||||
}
|
||||
}
|
||||
// write very last line segment if needed
|
||||
if (last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|
||||
{ // only add last element if boundary segment ends in odd scanline
|
||||
addLine(first_zigzag_connector[first_zigzag_connector.size() - 2], first_zigzag_connector[first_zigzag_connector.size() - 1]);
|
||||
}
|
||||
// reset member variables
|
||||
is_first_zigzag_connector = true;
|
||||
first_zigzag_connector_ends_in_even_scanline = true;
|
||||
last_scanline_is_even = false;
|
||||
first_zigzag_connector.clear();
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,26 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
|
||||
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ZigzagConnectorProcessorEndPieces.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class ZigzagConnectorProcessorDisconnectedEndPieces : public ZigzagConnectorProcessorEndPieces
|
||||
{
|
||||
|
||||
public:
|
||||
ZigzagConnectorProcessorDisconnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
|
||||
{
|
||||
}
|
||||
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
|
||||
void registerPolyFinished();
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
|
||||
@@ -0,0 +1,27 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "ZigzagConnectorProcessorEndPieces.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void ZigzagConnectorProcessorEndPieces::registerVertex(const Point& vertex)
|
||||
{
|
||||
if (is_first_zigzag_connector)
|
||||
{
|
||||
first_zigzag_connector.push_back(vertex);
|
||||
}
|
||||
else if (last_scanline_is_even)
|
||||
{ // when a boundary segments starts in an even scanline it's either a normal zigzag connector or an endpiece to be included
|
||||
// note that for ZigzagConnectorProcessorDisconnectedEndPieces only the last line segment from a boundary vertex to a scanline-boundary intersection is omitted
|
||||
addLine(last_connector_point, vertex);
|
||||
}
|
||||
else
|
||||
{ // it's yet unclear whether the line segment should be included, so we store it until we know
|
||||
zigzag_connector.push_back(vertex);
|
||||
}
|
||||
last_connector_point = vertex;
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,32 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
|
||||
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ActualZigzagConnectorProcessor.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
class ZigzagConnectorProcessorEndPieces : public ActualZigzagConnectorProcessor
|
||||
{
|
||||
protected:
|
||||
Point last_connector_point; //!< last registered boundary vertex or scanline-coundary intersection
|
||||
|
||||
ZigzagConnectorProcessorEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ActualZigzagConnectorProcessor(rotation_matrix, result)
|
||||
, last_connector_point(0,0)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void registerVertex(const Point& vertex);
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
|
||||
@@ -0,0 +1,72 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "ZigzagConnectorProcessorNoEndPieces.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void ZigzagConnectorProcessorNoEndPieces::registerVertex(const Point& vertex)
|
||||
{
|
||||
if (is_first_zigzag_connector)
|
||||
{
|
||||
first_zigzag_connector.push_back(vertex);
|
||||
}
|
||||
else if (last_scanline_is_even)
|
||||
{
|
||||
zigzag_connector.push_back(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ZigzagConnectorProcessorNoEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
|
||||
{
|
||||
bool previous_scanline_is_even = last_scanline_is_even;
|
||||
last_scanline_is_even = scanline_is_even;
|
||||
bool this_scanline_is_even = last_scanline_is_even; // for conceptual clarity
|
||||
|
||||
if (is_first_zigzag_connector)
|
||||
{
|
||||
first_zigzag_connector.push_back(intersection);
|
||||
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
|
||||
is_first_zigzag_connector = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (previous_scanline_is_even && !this_scanline_is_even)
|
||||
{ // add whole zigzag_connector (including the just obtained point)
|
||||
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
|
||||
}
|
||||
addLine(zigzag_connector.back(), intersection);
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
}
|
||||
zigzag_connector.clear(); // we're starting a new zigzag connector, so clear the old one
|
||||
if (this_scanline_is_even) // only boundary segments starting in an even segment are considered
|
||||
{
|
||||
zigzag_connector.push_back(intersection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ZigzagConnectorProcessorNoEndPieces::registerPolyFinished()
|
||||
{
|
||||
if (!is_first_zigzag_connector && last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|
||||
{ // only if it's a normal zigzag connector; not when the whole boundary didn't cross any scanlines
|
||||
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() ; point_idx++)
|
||||
{
|
||||
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
|
||||
}
|
||||
}
|
||||
// reset member variables
|
||||
is_first_zigzag_connector = true;
|
||||
first_zigzag_connector_ends_in_even_scanline = true;
|
||||
last_scanline_is_even = false;
|
||||
first_zigzag_connector.clear();
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,29 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
|
||||
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ActualZigzagConnectorProcessor.h"
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class ZigzagConnectorProcessorNoEndPieces : public ActualZigzagConnectorProcessor
|
||||
{
|
||||
public:
|
||||
ZigzagConnectorProcessorNoEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ActualZigzagConnectorProcessor(rotation_matrix, result)
|
||||
{
|
||||
}
|
||||
|
||||
void registerVertex(const Point& vertex);
|
||||
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
|
||||
void registerPolyFinished();
|
||||
};
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
|
||||
+2
-2
@@ -16,7 +16,7 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
|
||||
part->insets.push_back(Polygons());
|
||||
if (i == 0)
|
||||
{
|
||||
if (line_width_0 < nozzle_width)
|
||||
if (false) // line_width_0 < nozzle_width) // TODO: this is a quick fix for version 2.1 only; this line should not be in master
|
||||
{
|
||||
PolygonUtils::offsetSafe(part->outline, - nozzle_width/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
|
||||
}
|
||||
@@ -26,7 +26,7 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
|
||||
}
|
||||
} else if (i == 1)
|
||||
{
|
||||
if (line_width_0 < nozzle_width)
|
||||
if (false) // line_width_0 < nozzle_width) // TODO: this is a quick fix for version 2.1 only; this line should not be in master
|
||||
{
|
||||
int offset_from_first_boundary_for_edge_of_outer_wall = -nozzle_width/2;
|
||||
// ideally this /\ should be: nozzle_width/2 - line_width_0; however, factually, the nozzle will fill up part of the perimeter gaps
|
||||
|
||||
+23
-20
@@ -66,7 +66,7 @@ void print_call(int argc, char **argv)
|
||||
|
||||
void connect(int argc, char **argv)
|
||||
{
|
||||
CommandSocket* commandSocket = new CommandSocket();
|
||||
CommandSocket::instantiate();
|
||||
std::string ip;
|
||||
int port = 49674;
|
||||
|
||||
@@ -107,7 +107,7 @@ void connect(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
commandSocket->connect(ip, port);
|
||||
CommandSocket::getInstance()->connect(ip, port);
|
||||
}
|
||||
|
||||
void slice(int argc, char **argv)
|
||||
@@ -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,25 +134,27 @@ 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_extruder_train = meshgroup->createExtruderTrain(0);
|
||||
last_settings_object = meshgroup;
|
||||
|
||||
}catch(...){
|
||||
cura::logError("Unknown exception\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}else{
|
||||
cura::logError("Unknown option: %s\n", str);
|
||||
}
|
||||
@@ -177,8 +179,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 +188,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 +206,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 +242,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 +251,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 +265,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
|
||||
|
||||
+96
-105
@@ -2,6 +2,7 @@
|
||||
#include "pathOrderOptimizer.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/BucketGrid2D.h"
|
||||
#include "utils/linearAlg2D.h"
|
||||
|
||||
#define INLINE static inline
|
||||
|
||||
@@ -15,17 +16,16 @@ void PathOrderOptimizer::optimize()
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
PolygonRef poly = polygons[i_polygon];
|
||||
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point in polygon
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
|
||||
{
|
||||
float dist = vSize2f(poly[i_point] - startPoint);
|
||||
float dist = vSize2f(poly[point_idx] - startPoint);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i_point;
|
||||
best = point_idx;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
@@ -37,46 +37,50 @@ void PathOrderOptimizer::optimize()
|
||||
|
||||
|
||||
Point prev_point = startPoint;
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
|
||||
for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
|
||||
{
|
||||
int best = -1;
|
||||
int best_poly_idx = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
|
||||
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
assert (polygons[i_polygon].size() != 2);
|
||||
assert (polygons[poly_idx].size() != 2);
|
||||
|
||||
float dist = vSize2f(polygons[i_polygon][polyStart[i_polygon]] - prev_point);
|
||||
float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i_polygon;
|
||||
best_poly_idx = poly_idx;
|
||||
bestDist = dist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
assert(polygons[best].size() != 2);
|
||||
assert(polygons[best_poly_idx].size() != 2);
|
||||
|
||||
prev_point = polygons[best][polyStart[best]];
|
||||
prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];
|
||||
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
picked[best_poly_idx] = true;
|
||||
polyOrder.push_back(best_poly_idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("Failed to find next closest polygon.\n");
|
||||
}
|
||||
}
|
||||
|
||||
prev_point = startPoint;
|
||||
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
|
||||
for (unsigned int order_idx = 0; order_idx < polyOrder.size(); order_idx++) /// decide final starting points in each polygon
|
||||
{
|
||||
int poly_idx = polyOrder[n];
|
||||
int poly_idx = polyOrder[order_idx];
|
||||
int point_idx = getPolyStart(prev_point, poly_idx);
|
||||
polyStart[poly_idx] = point_idx;
|
||||
prev_point = polygons[poly_idx][point_idx];
|
||||
@@ -99,22 +103,23 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
|
||||
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
|
||||
{
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
|
||||
int best_point_idx = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
bool orientation = poly.orientation();
|
||||
for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++)
|
||||
float best_point_score = std::numeric_limits<float>::infinity();
|
||||
Point p0 = poly.back();
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
float dist = vSize2f(poly[i_point] - prev_point);
|
||||
Point n0 = normal(poly[(i_point-1+poly.size())%poly.size()] - poly[i_point], 2000);
|
||||
Point n1 = normal(poly[i_point] - poly[(i_point + 1) % poly.size()], 2000);
|
||||
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht
|
||||
if (orientation)
|
||||
dot_score = -dot_score;
|
||||
if (dist + dot_score < bestDist)
|
||||
Point& p1 = poly[point_idx];
|
||||
Point& p2 = poly[(point_idx + 1) % poly.size()];
|
||||
int64_t dist = vSize2(p1 - prev_point);
|
||||
float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners
|
||||
// this score is in the order of 5 mm
|
||||
if (dist + is_on_inside_corner_score < best_point_score)
|
||||
{
|
||||
best_point_idx = i_point;
|
||||
bestDist = dist;
|
||||
best_point_idx = point_idx;
|
||||
best_point_score = dist + is_on_inside_corner_score;
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
return best_point_idx;
|
||||
}
|
||||
@@ -152,127 +157,113 @@ void LineOrderOptimizer::optimize()
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
PolygonRef poly = polygons[i_polygon];
|
||||
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point from polygon
|
||||
int best_point_idx = -1;
|
||||
float best_point_dist = std::numeric_limits<float>::infinity();
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
|
||||
{
|
||||
float dist = vSize2f(poly[i_point] - startPoint);
|
||||
if (dist < bestDist)
|
||||
float dist = vSize2f(poly[point_idx] - startPoint);
|
||||
if (dist < best_point_dist)
|
||||
{
|
||||
best = i_point;
|
||||
bestDist = dist;
|
||||
best_point_idx = point_idx;
|
||||
best_point_dist = dist;
|
||||
}
|
||||
}
|
||||
polyStart.push_back(best);
|
||||
polyStart.push_back(best_point_idx);
|
||||
|
||||
assert(poly.size() == 2);
|
||||
|
||||
line_bucket_grid.insert(poly[0], i_polygon);
|
||||
line_bucket_grid.insert(poly[1], i_polygon);
|
||||
line_bucket_grid.insert(poly[0], poly_idx);
|
||||
line_bucket_grid.insert(poly[1], poly_idx);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Point incommingPerpundicularNormal(0, 0);
|
||||
Point incoming_perpundicular_normal(0, 0);
|
||||
Point prev_point = startPoint;
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
|
||||
for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
int best_line_idx = -1;
|
||||
float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line
|
||||
|
||||
for(unsigned int i_close_line_polygon : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
|
||||
for(unsigned int close_line_poly_idx : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
|
||||
{
|
||||
if (picked[i_close_line_polygon] || polygons[i_close_line_polygon].size() < 1)
|
||||
if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
checkIfLineIsBest(i_close_line_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
|
||||
|
||||
updateBestLine(close_line_poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
|
||||
}
|
||||
|
||||
if (best == -1) /// if single-line-polygon hasn't been found yet
|
||||
if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
|
||||
{
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
|
||||
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
|
||||
{
|
||||
continue;
|
||||
assert(polygons[i_polygon].size() == 2);
|
||||
}
|
||||
assert(polygons[poly_idx].size() == 2);
|
||||
|
||||
checkIfLineIsBest(i_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
|
||||
updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
assert(polygons[best].size() == 2);
|
||||
PolygonRef best_line = polygons[best_line_idx];
|
||||
assert(best_line.size() == 2);
|
||||
|
||||
int endIdx = polyStart[best] * -1 + 1; /// 1 -> 0 , 0 -> 1
|
||||
prev_point = polygons[best][endIdx];
|
||||
incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000));
|
||||
int line_start_point_idx = polyStart[best_line_idx];
|
||||
int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
|
||||
Point& line_start = best_line[line_start_point_idx];
|
||||
Point& line_end = best_line[line_end_point_idx];
|
||||
prev_point = line_end;
|
||||
incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));
|
||||
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
picked[best_line_idx] = true;
|
||||
polyOrder.push_back(best_line_idx);
|
||||
}
|
||||
else
|
||||
logError("Failed to find next closest line.\n");
|
||||
}
|
||||
|
||||
prev_point = startPoint;
|
||||
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
|
||||
{
|
||||
int nr = polyOrder[n];
|
||||
PolygonRef poly = polygons[nr];
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
bool orientation = poly.orientation();
|
||||
for(unsigned int i=0;i<poly.size(); i++)
|
||||
{
|
||||
float dist = vSize2f(polygons[nr][i] - prev_point);
|
||||
Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], 2000);
|
||||
Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], 2000);
|
||||
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
|
||||
if (orientation)
|
||||
dot_score = -dot_score;
|
||||
if (dist + dot_score < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist + dot_score;
|
||||
}
|
||||
logError("Failed to find next closest line.\n");
|
||||
}
|
||||
|
||||
polyStart[nr] = best;
|
||||
assert(poly.size() == 2);
|
||||
prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal)
|
||||
inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal)
|
||||
{
|
||||
Point& p0 = polygons[poly_idx][0];
|
||||
Point& p1 = polygons[poly_idx][1];
|
||||
float dot_score = getAngleScore(incoming_perpundicular_normal, p0, p1);
|
||||
{ /// check distance to first point on line (0)
|
||||
float dist = vSize2f(polygons[i_line_polygon][0] - prev_point);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][1] - polygons[i_line_polygon][0], 1000))) * 0.0001f; /// penalize sharp corners
|
||||
if (dist < bestDist)
|
||||
float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
|
||||
if (score < best_score)
|
||||
{
|
||||
best = i_line_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i_line_polygon] = 0;
|
||||
best = poly_idx;
|
||||
best_score = score;
|
||||
polyStart[poly_idx] = 0;
|
||||
}
|
||||
}
|
||||
{ /// check distance to second point on line (1)
|
||||
float dist = vSize2f(polygons[i_line_polygon][1] - prev_point);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][0] - polygons[i_line_polygon][1], 1000) )) * 0.0001f; /// penalize sharp corners
|
||||
if (dist < bestDist)
|
||||
float score = vSize2f(p1 - prev_point) + dot_score; // prefer 90 degree corners
|
||||
if (score < best_score)
|
||||
{
|
||||
best = i_line_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i_line_polygon] = 1;
|
||||
best = poly_idx;
|
||||
best_score = score;
|
||||
polyStart[poly_idx] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float LineOrderOptimizer::getAngleScore(Point incoming_perpundicular_normal, Point p0, Point p1)
|
||||
{
|
||||
return dot(incoming_perpundicular_normal, normal(p1 - p0, 1000)) * 0.0001f;
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -81,8 +81,35 @@ public:
|
||||
void optimize(); //!< sets #polyStart and #polyOrder
|
||||
|
||||
private:
|
||||
void checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal);
|
||||
/*!
|
||||
* Update LineOrderOptimizer::polyStart if the current line is better than the current best.
|
||||
*
|
||||
* Besides looking at the distance from the previous line segment, we also look at the angle we make.
|
||||
*
|
||||
* We prefer 90 degree angles; 180 degree turn arounds are slow on machines where the jerk is limited.
|
||||
* 0 degree (straight ahead) 'corners' occur only when a single infill line is interrupted,
|
||||
* in which case the travel move might involve combing, which makes it rather longer.
|
||||
*
|
||||
* \param poly_idx[in] The index in LineOrderOptimizer::polygons for the current line to test
|
||||
* \param best[in, out] The index of current best line
|
||||
* \param best_score[in, out] The distance score for the current best line
|
||||
* \param prev_point[in] The previous point from which to find the next best line
|
||||
* \param incoming_perpundicular_normal[in] The direction of movement when the print head arrived at \p prev_point, turned 90 degrees CCW
|
||||
*/
|
||||
void updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal);
|
||||
|
||||
/*!
|
||||
* Get a score to modify the distance score for measuring how good two lines follow each other.
|
||||
*
|
||||
* The angle score is symmetric in \p from and \p to; they can be exchanged without altering the result. (Code relies on this property)
|
||||
*
|
||||
* \param incoming_perpundicular_normal The direction in which the head was moving while printing the previous line, turned 90 degrees CCW
|
||||
* \param from The one end of the next line
|
||||
* \param to The other end of the next line
|
||||
* \return A score measuring how good the angle is of the line between \p from and \p to when the previous line had a direction given by \p incoming_perpundicular_normal
|
||||
*
|
||||
*/
|
||||
static float getAngleScore(Point incoming_perpundicular_normal, Point from, Point to);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+5
-3
@@ -1,4 +1,6 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <clipper/clipper.hpp>
|
||||
|
||||
#include "raft.h"
|
||||
#include "support.h"
|
||||
|
||||
@@ -8,15 +10,15 @@ void generateRaft(SliceDataStorage& storage, int distance)
|
||||
{
|
||||
if (storage.draft_protection_shield.size() > 0)
|
||||
{
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance));
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance, ClipperLib::jtRound));
|
||||
}
|
||||
else if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0)
|
||||
{
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance));
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance, ClipperLib::jtRound));
|
||||
}
|
||||
else
|
||||
{
|
||||
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance);
|
||||
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance, ClipperLib::jtRound);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+74
-43
@@ -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,22 +195,36 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,45 +236,52 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
|
||||
{
|
||||
std::string setting = override_iterator->name.GetString();
|
||||
SettingConfig* conf = getSettingConfig(setting);
|
||||
if (!conf) //Setting could not be found.
|
||||
{
|
||||
logWarning("Trying to override unknown setting %s.", setting.c_str());
|
||||
continue;
|
||||
}
|
||||
_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;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/// Create the new setting config object.
|
||||
SettingConfig* config = parent->addChild(json_object_it->name.GetString(), label);
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
@@ -314,5 +346,4 @@ void SettingRegistry::_loadSettingValues(SettingConfig* config, rapidjson::Gener
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
}//namespace cura
|
||||
|
||||
+33
-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,6 +205,14 @@ 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
|
||||
*/
|
||||
|
||||
@@ -200,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);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "utils/floatpoint.h"
|
||||
|
||||
#include "FlowTempGraph.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
@@ -177,6 +179,10 @@ 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);
|
||||
|
||||
+25
-12
@@ -8,9 +8,9 @@ namespace cura
|
||||
{
|
||||
|
||||
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, 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, innermost_wall_extrusion_width, downSkinCount, upSkinCount, no_small_gaps_heuristic);
|
||||
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,7 +20,7 @@ void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, i
|
||||
}
|
||||
}
|
||||
|
||||
void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int downSkinCount, int upSkinCount, bool no_small_gaps_heuristic)
|
||||
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[layer_nr];
|
||||
|
||||
@@ -32,18 +32,26 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
|
||||
for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
|
||||
{
|
||||
SliceLayerPart& part = layer.parts[partNr];
|
||||
|
||||
|
||||
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 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)
|
||||
auto getInsidePolygons = [&part, wall_line_count](SliceLayer& layer2)
|
||||
{
|
||||
Polygons result;
|
||||
for(SliceLayerPart& part2 : layer2.parts)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
result.add(part2.insets.back());
|
||||
{
|
||||
unsigned int wall_idx = std::min(wall_line_count, (int) part2.insets.size()) - 1;
|
||||
result.add(part2.insets[wall_idx]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -62,20 +70,20 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
|
||||
}
|
||||
else
|
||||
{
|
||||
if (layer_nr > 0 && downSkinCount > 0)
|
||||
if (layer_nr >= downSkinCount && downSkinCount > 0)
|
||||
{
|
||||
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++)
|
||||
for (int downskin_layer_nr = 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)
|
||||
if (layer_nr < static_cast<int>(storage.layers.size()) - upSkinCount && 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++)
|
||||
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
|
||||
{
|
||||
not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
|
||||
}
|
||||
@@ -129,13 +137,18 @@ 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 innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count)
|
||||
{
|
||||
SliceLayer& layer = storage.layers[layerNr];
|
||||
|
||||
for(SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
Polygons infill = part.insets.back().offset(-extrusionWidth / 2 - infill_skin_overlap);
|
||||
if (int(part.insets.size()) < wall_line_count)
|
||||
{
|
||||
part.infill_area.emplace_back(); // put empty polygon as (uncombined) infill
|
||||
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
|
||||
}
|
||||
Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);
|
||||
|
||||
for(SliceLayerPart& part2 : layer.parts)
|
||||
{
|
||||
|
||||
+13
-5
@@ -26,13 +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 innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, 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)
|
||||
@@ -42,9 +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, bool no_small_gaps_heuristic);
|
||||
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.
|
||||
@@ -59,13 +61,19 @@ 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.
|
||||
*
|
||||
* After this function has been called on a layer of a mesh, each SliceLayerPart of that layer should have an infill_area consisting of exactly one Polygons : the normal uncombined infill area.
|
||||
*
|
||||
* \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 innermost_wall_extrusion_width width of the innermost 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 generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count);
|
||||
|
||||
/*!
|
||||
* \brief Combines the infill of multiple layers for a specified mesh.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "sliceDataStorage.h"
|
||||
|
||||
#include "FffProcessor.h" //To create a mesh group with if none is provided.
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
@@ -25,6 +27,48 @@ void SliceLayer::getOutlines(Polygons& result, bool external_polys_only)
|
||||
}
|
||||
}
|
||||
|
||||
Polygons SliceLayer::getSecondOrInnermostWalls()
|
||||
{
|
||||
Polygons ret;
|
||||
getSecondOrInnermostWalls(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls)
|
||||
{
|
||||
for (SliceLayerPart& part : parts)
|
||||
{
|
||||
// we want the 2nd inner walls
|
||||
if (part.insets.size() >= 2) {
|
||||
layer_walls.add(part.insets[1]);
|
||||
continue;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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, PrintFeatureType::MoveCombing),
|
||||
skirt_config(initializeSkirtConfigs()),
|
||||
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
|
||||
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
|
||||
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
|
||||
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_extruder_nr")], PrintFeatureType::Support),
|
||||
support_roof_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_roof_extruder_nr")], PrintFeatureType::Skin),
|
||||
max_object_height_second_to_last_extruder(-1)
|
||||
{
|
||||
}
|
||||
|
||||
Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only)
|
||||
{
|
||||
@@ -77,6 +121,44 @@ Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_pa
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+59
-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"
|
||||
@@ -51,8 +52,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);
|
||||
};
|
||||
|
||||
/******************/
|
||||
@@ -91,15 +119,15 @@ public:
|
||||
std::vector<GCodePathConfig> infill_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, PrintFeatureType::OuterWall), insetX_config(&retraction_config, PrintFeatureType::InnerWall), skin_config(&retraction_config, PrintFeatureType::Skin)
|
||||
{
|
||||
infill_config.reserve(MAX_INFILL_COMBINE);
|
||||
for(int n=0; n<MAX_INFILL_COMBINE; n++)
|
||||
infill_config.emplace_back(&retraction_config, "FILL");
|
||||
infill_config.emplace_back(&retraction_config, PrintFeatureType::Infill);
|
||||
}
|
||||
};
|
||||
|
||||
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 +138,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;
|
||||
|
||||
@@ -140,21 +173,22 @@ public:
|
||||
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
|
||||
{
|
||||
RetractionConfig* extruder_retraction_config = &retraction_config_per_extruder[extruder];
|
||||
skirt_config.emplace_back(extruder_retraction_config, "SKIRT");
|
||||
skirt_config.emplace_back(extruder_retraction_config, PrintFeatureType::Skirt);
|
||||
}
|
||||
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()
|
||||
{
|
||||
@@ -168,6 +202,16 @@ public:
|
||||
* \param external_polys_only whether to disregard all hole polygons
|
||||
*/
|
||||
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);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+38
-17
@@ -35,7 +35,7 @@ Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_thi
|
||||
return joined;
|
||||
}
|
||||
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket)
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count)
|
||||
{
|
||||
// initialization of supportAreasPerLayer
|
||||
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
|
||||
@@ -46,11 +46,11 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
std::vector<Polygons> supportAreas;
|
||||
supportAreas.resize(layer_count, Polygons());
|
||||
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas, commandSocket);
|
||||
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas);
|
||||
|
||||
if (mesh.getSettingBoolean("support_roof_enable"))
|
||||
{
|
||||
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"), commandSocket);
|
||||
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -80,7 +80,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
|
||||
*
|
||||
* for support buildplate only: purge all support not connected to buildplate
|
||||
*/
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, 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)
|
||||
{
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
|
||||
@@ -94,7 +94,6 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
|
||||
double supportAngle = mesh.getSettingInAngleRadians("support_angle");
|
||||
bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
|
||||
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");
|
||||
@@ -114,21 +113,23 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
|
||||
int layerThickness = mesh.getSettingInMicrons("layer_height");
|
||||
int extrusionWidth = mesh.getSettingInMicrons("support_line_width");
|
||||
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
|
||||
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
|
||||
|
||||
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:
|
||||
if (conical_support_angle == 0)
|
||||
{
|
||||
conical_support = false;
|
||||
}
|
||||
|
||||
if (supportZDistanceBottom < 0) supportZDistanceBottom = supportZDistance;
|
||||
if (supportZDistanceTop < 0) supportZDistanceTop = supportZDistance;
|
||||
// derived settings:
|
||||
|
||||
|
||||
int supportLayerThickness = layerThickness;
|
||||
|
||||
int layerZdistanceTop = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang
|
||||
int layerZdistanceTop = std::max(0, supportZDistanceTop / supportLayerThickness) + 1; // support must always be 1 layer below overhang
|
||||
unsigned int layerZdistanceBottom = std::max(0, supportZDistanceBottom / supportLayerThickness);
|
||||
|
||||
double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle
|
||||
@@ -136,12 +137,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
|
||||
int64_t conical_support_offset;
|
||||
if (conical_support_angle > 0)
|
||||
{
|
||||
conical_support_offset = (tan(conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
{ // outward ==> wider base than overhang
|
||||
conical_support_offset = -(tan(conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
}
|
||||
else
|
||||
{
|
||||
conical_support_offset = -(tan(-conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
{ // inward ==> smaller base than overhang
|
||||
conical_support_offset = (tan(-conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
}
|
||||
|
||||
unsigned int support_layer_count = layer_count;
|
||||
@@ -248,11 +249,11 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
|
||||
if (still_in_upper_empty_layers && supportLayer_this.size() > 0)
|
||||
{
|
||||
storage.support.layer_nr_max_filled_layer = layer_idx;
|
||||
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, (int)layer_idx);
|
||||
still_in_upper_empty_layers = false;
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size(), commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size());
|
||||
}
|
||||
|
||||
// do stuff for when support on buildplate only
|
||||
@@ -263,6 +264,26 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
{
|
||||
Polygons& supportLayer = supportAreas[layer_idx];
|
||||
|
||||
if (conical_support)
|
||||
{ // with conical support the next layer is allowed to be larger than the previous
|
||||
touching_buildplate = touching_buildplate.offset(std::abs(conical_support_offset) + 10, ClipperLib::jtMiter, 10);
|
||||
// + 10 and larger miter limit cause performing an outward offset after an inward offset can disregard sharp corners
|
||||
//
|
||||
// conical support can make
|
||||
// layer above layer below
|
||||
// v v
|
||||
// | : |
|
||||
// | ==> : |__
|
||||
// |____ :....
|
||||
//
|
||||
// a miter limit would result in
|
||||
// | : : |
|
||||
// | :.. <== : |__
|
||||
// .\___ :....
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease!
|
||||
|
||||
supportAreas[layer_idx] = touching_buildplate;
|
||||
@@ -409,7 +430,7 @@ void AreaSupport::handleWallStruts(
|
||||
}
|
||||
|
||||
|
||||
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket)
|
||||
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height)
|
||||
{
|
||||
int roof_layer_count = support_roof_height / layerThickness;
|
||||
|
||||
|
||||
+3
-6
@@ -15,9 +15,8 @@ public:
|
||||
* Generate the support areas and support roof areas for all models.
|
||||
* \param storage data storage containing the input layer outline data and containing the output support storage per layer
|
||||
* \param layer_count total number of layers
|
||||
* \param commandSocket Socket over which to report the progress
|
||||
*/
|
||||
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket);
|
||||
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count);
|
||||
|
||||
private:
|
||||
/*!
|
||||
@@ -28,9 +27,8 @@ private:
|
||||
* \param storage data storage containing the input layer outline data
|
||||
* \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, unsigned int mesh_idx, 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);
|
||||
|
||||
|
||||
|
||||
@@ -39,11 +37,10 @@ private:
|
||||
*
|
||||
* \param storage Output storage: support area + support roof area output
|
||||
* \param supportAreas The basic support areas for the current mesh
|
||||
* \param commandSocket Socket over which to report the progress
|
||||
* \param layerThickness The layer height
|
||||
* \param support_roof_height The thickness of the hammock in z directiontt
|
||||
*/
|
||||
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket);
|
||||
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height);
|
||||
|
||||
/*!
|
||||
* Join current support layer with the support of the layer above, (make support conical) and perform smoothing etc operations.
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "linearAlg2D.h"
|
||||
|
||||
#include <cmath> // atan2
|
||||
|
||||
#include "intpoint.h" // dot
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c)
|
||||
{
|
||||
Point ba = a - b;
|
||||
Point bc = c - b;
|
||||
int64_t dott = dot(ba, bc); // dot product
|
||||
int64_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant
|
||||
float angle = -atan2(det, dott); // from -pi to pi
|
||||
if (angle >= 0 )
|
||||
{
|
||||
return angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
return M_PI * 2 + angle;
|
||||
}
|
||||
// Point ba = a - b;
|
||||
// Point bc = c - b;
|
||||
// int64_t dott = dot(ba, bc); // dot product
|
||||
// int64_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant
|
||||
// return -atan2(det, dott); // from -pi to pi
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
+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&);
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -276,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); }
|
||||
@@ -198,7 +202,7 @@ INLINE Point normal(const Point& p0, int64_t len)
|
||||
return p0 * len / _len;
|
||||
}
|
||||
|
||||
INLINE Point crossZ(const Point& p0)
|
||||
INLINE Point turn90CCW(const Point& p0)
|
||||
{
|
||||
return Point(-p0.Y, p0.X);
|
||||
}
|
||||
@@ -307,3 +311,4 @@ inline Point operator-(const Point& p2, const Point3& p3) {
|
||||
|
||||
}//namespace cura
|
||||
#endif//INT_POINT_H
|
||||
|
||||
|
||||
@@ -103,6 +103,8 @@ 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
|
||||
@@ -127,9 +129,18 @@ 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;
|
||||
|
||||
@@ -144,13 +155,51 @@ public:
|
||||
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
|
||||
}
|
||||
|
||||
/*!
|
||||
* Checks whether the minimal distance between two line segments is at most \p max_dist
|
||||
* The first line semgent is given by end points \p a and \p b, the second by \p c and \p d.
|
||||
*
|
||||
* \param a One end point of the first line segment
|
||||
* \param b Another end point of the first line segment
|
||||
* \param c One end point of the second line segment
|
||||
* \param d Another end point of the second line segment
|
||||
* \param max_dist The maximal distance between the two line segments for which this function will return true.
|
||||
*/
|
||||
static bool lineSegmentsAreCloserThan(const Point& a, const Point& b, const Point& c, const Point& d, int64_t max_dist)
|
||||
{
|
||||
int64_t max_dist2 = max_dist * max_dist;
|
||||
|
||||
return getDist2FromLineSegment(a, c, b) <= max_dist2
|
||||
|| getDist2FromLineSegment(a, d, b) <= max_dist2
|
||||
|| getDist2FromLineSegment(c, a, d) <= max_dist2
|
||||
|| getDist2FromLineSegment(c, b, d) <= max_dist2;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Compute the angle between two consecutive line segments.
|
||||
*
|
||||
* The angle is computed from the left side of b when looking from a.
|
||||
*
|
||||
* c
|
||||
* \ .
|
||||
* \ b
|
||||
* angle|
|
||||
* |
|
||||
* a
|
||||
*
|
||||
* \param a start of first line segment
|
||||
* \param b end of first segment and start of second line segment
|
||||
* \param c end of second line segment
|
||||
* \return the angle in radians between 0 and 2 * pi of the corner in \p b
|
||||
*/
|
||||
static float getAngleLeft(const Point& a, const Point& b, const Point& c);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -162,7 +162,13 @@ void PolygonRef::simplify(int smallest_line_segment_squared, int allowed_error_d
|
||||
}
|
||||
polygon->erase(polygon->begin() + writing_idx , polygon->end());
|
||||
}
|
||||
|
||||
|
||||
if (size() < 3)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
Point* last = &thiss[0];
|
||||
unsigned int writing_idx = 1;
|
||||
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
|
||||
@@ -174,7 +180,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
|
||||
|
||||
+24
-22
@@ -21,20 +21,6 @@
|
||||
|
||||
namespace cura {
|
||||
|
||||
enum PolygonType
|
||||
{
|
||||
NoneType,
|
||||
Inset0Type,
|
||||
InsetXType,
|
||||
SkinType,
|
||||
SupportType,
|
||||
SkirtType,
|
||||
InfillType,
|
||||
SupportInfillType,
|
||||
MoveCombingType,
|
||||
MoveRetractionType
|
||||
};
|
||||
|
||||
|
||||
class PartsView;
|
||||
|
||||
@@ -79,7 +65,9 @@ public:
|
||||
}
|
||||
|
||||
PolygonRef& operator=(const PolygonRef& other) { polygon = other.polygon; return *this; }
|
||||
|
||||
|
||||
bool operator==(const PolygonRef& other) const =delete;
|
||||
|
||||
ClipperLib::Path& operator*() { return *polygon; }
|
||||
|
||||
template <typename... Args>
|
||||
@@ -225,7 +213,7 @@ public:
|
||||
|
||||
/*!
|
||||
* Smooth out the polygon and store the result in \p result.
|
||||
* Smoothing is performed by removing line segments smaller than \p remove_length
|
||||
* Smoothing is performed by removing vertices for which both connected line segments are smaller than \p remove_length
|
||||
*
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
@@ -235,15 +223,26 @@ public:
|
||||
PolygonRef& thiss = *this;
|
||||
ClipperLib::Path* poly = result.polygon;
|
||||
if (size() > 0)
|
||||
{
|
||||
poly->push_back(thiss[0]);
|
||||
}
|
||||
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
|
||||
{
|
||||
if (shorterThen(thiss[poly_idx-1]-thiss[poly_idx], remove_length))
|
||||
Point& last = thiss[poly_idx - 1];
|
||||
Point& now = thiss[poly_idx];
|
||||
Point& next = thiss[(poly_idx + 1) % size()];
|
||||
if (shorterThen(last - now, remove_length) && shorterThen(now - next, remove_length))
|
||||
{
|
||||
poly_idx++; // skip the next line piece (dont escalate the removal of edges)
|
||||
if (poly_idx < size())
|
||||
{
|
||||
poly->push_back(thiss[poly_idx]);
|
||||
} else poly->push_back(thiss[poly_idx]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
poly->push_back(thiss[poly_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,8 +358,12 @@ public:
|
||||
}
|
||||
|
||||
Polygons() {}
|
||||
|
||||
Polygons(const Polygons& other) { polygons = other.polygons; }
|
||||
Polygons& operator=(const Polygons& other) { polygons = other.polygons; return *this; }
|
||||
|
||||
bool operator==(const Polygons& other) const =delete;
|
||||
|
||||
Polygons difference(const Polygons& other) const
|
||||
{
|
||||
Polygons ret;
|
||||
@@ -404,13 +407,12 @@ public:
|
||||
clipper.Execute(ClipperLib::ctXor, ret.polygons);
|
||||
return ret;
|
||||
}
|
||||
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter) const
|
||||
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const
|
||||
{
|
||||
Polygons ret;
|
||||
double miterLimit = 1.2;
|
||||
ClipperLib::ClipperOffset clipper(miterLimit, 10.0);
|
||||
ClipperLib::ClipperOffset clipper(miter_limit, 10.0);
|
||||
clipper.AddPaths(polygons, joinType, ClipperLib::etClosedPolygon);
|
||||
clipper.MiterLimit = miterLimit;
|
||||
clipper.MiterLimit = miter_limit;
|
||||
clipper.Execute(ret.polygons, distance);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ Point PolygonUtils::getBoundaryPointWithOffset(PolygonRef poly, unsigned int poi
|
||||
Point p1 = poly[point_idx];
|
||||
Point p2 = poly[(point_idx < (poly.size() - 1)) ? (point_idx + 1) : 0];
|
||||
|
||||
Point off0 = crossZ(normal(p1 - p0, MM2INT(1.0))); // 1.0 for some precision
|
||||
Point off1 = crossZ(normal(p2 - p1, MM2INT(1.0))); // 1.0 for some precision
|
||||
Point off0 = turn90CCW(normal(p1 - p0, MM2INT(1.0))); // 1.0 for some precision
|
||||
Point off1 = turn90CCW(normal(p2 - p1, MM2INT(1.0))); // 1.0 for some precision
|
||||
Point n = normal(off0 + off1, -offset);
|
||||
|
||||
return p1 + n;
|
||||
@@ -85,7 +85,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
Point ret = from;
|
||||
int64_t bestDist2 = std::numeric_limits<int64_t>::max();
|
||||
unsigned int bestPoly = NO_INDEX;
|
||||
bool is_inside = false;
|
||||
bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
@@ -125,9 +125,10 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
if (distance == 0) { ret = x; }
|
||||
else
|
||||
{
|
||||
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;
|
||||
Point inward_dir = turn90CCW(normal(ab, MM2INT(10.0)) + normal(p1 - p0, MM2INT(10.0))); // inward direction irrespective of sign of [distance]
|
||||
// MM2INT(10.0) to retain precision for the eventual normalization
|
||||
ret = x + normal(inward_dir, distance);
|
||||
is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) * distance >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,7 +148,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
{ // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
|
||||
projected_p_beyond_prev_segment = false;
|
||||
Point x = a + ab * ax_length / ab_length;
|
||||
|
||||
@@ -159,9 +160,9 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
if (distance == 0) { ret = x; }
|
||||
else
|
||||
{
|
||||
Point inward_dir = crossZ(normal(ab, distance));
|
||||
Point inward_dir = turn90CCW(normal(ab, distance)); // inward or outward depending on the sign of [distance]
|
||||
ret = x + inward_dir;
|
||||
is_inside = dot(inward_dir, p - x) >= 0;
|
||||
is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,7 +172,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
p1 = p2;
|
||||
}
|
||||
}
|
||||
if (is_inside)
|
||||
if (is_already_on_correct_side_of_boundary) // when the best point is already inside and we're moving inside, or when the best point is already outside and we're moving outside
|
||||
{
|
||||
if (bestDist2 < distance * distance)
|
||||
{
|
||||
@@ -191,7 +192,6 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
return NO_INDEX;
|
||||
}
|
||||
|
||||
|
||||
void PolygonUtils::findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size)
|
||||
{
|
||||
PolygonRef poly1 = poly1_result.poly;
|
||||
@@ -454,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;
|
||||
@@ -466,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;
|
||||
|
||||
+66
-34
@@ -66,10 +66,10 @@ public:
|
||||
* \param polygons The polygons onto which to move the point
|
||||
* \param from The point to move.
|
||||
* \param distance The distance by which to move the point.
|
||||
* \param maxDist2 The squared maximal allowed distance from the point to the nearest polygon.
|
||||
* \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon.
|
||||
* \return The index to the polygon onto which we have moved the point.
|
||||
*/
|
||||
static unsigned int moveInside(Polygons& polygons, Point& from, int distance = 0, int64_t maxDist2 = std::numeric_limits<int64_t>::max());
|
||||
static unsigned int moveInside(Polygons& polygons, Point& from, int distance = 0, int64_t max_dist2 = std::numeric_limits<int64_t>::max());
|
||||
|
||||
/*!
|
||||
* Find the two points in two polygons with the smallest distance.
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int
|
||||
Point& last_point = *last_it;
|
||||
Point& point = *it;
|
||||
|
||||
if ( from_it.poly == to_list_poly
|
||||
if (&from_it.poly == &to_list_poly
|
||||
&& (
|
||||
(from_it.it == last_it || from_it.it == it) // we currently consider a linesegment directly connected to [from]
|
||||
|| (from_it.prev().it == it || from_it.next().it == last_it) // line segment from [last_point] to [point] is connected to line segment of which [from] is the other end
|
||||
@@ -94,7 +94,7 @@ void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int
|
||||
int64_t dist2 = vSize2(closest - from);
|
||||
|
||||
if (dist2 > line_width * line_width
|
||||
|| ( from_it.poly == to_list_poly
|
||||
|| (&from_it.poly == &to_list_poly
|
||||
&& dot(from_it.next().p() - from, point - last_point) > 0
|
||||
&& dot(from - from_it.prev().p(), point - last_point) > 0 ) // line segments are likely connected, because the winding order is in the same general direction
|
||||
)
|
||||
|
||||
+10
-1
@@ -74,7 +74,16 @@ class WallOverlapComputation
|
||||
ListPolyIt(ListPolygon& poly, ListPolygon::iterator it)
|
||||
: poly(poly), it(it) { }
|
||||
Point& p() const { return *it; }
|
||||
bool operator==(const ListPolyIt& other) const { return poly == other.poly && it == other.it; }
|
||||
/*!
|
||||
* Test whether two iterators refer to the same polygon in the same polygon list.
|
||||
*
|
||||
* \param other The ListPolyIt to test for equality
|
||||
* \return Wether the right argument refers to the same polygon in the same ListPolygon as the left argument.
|
||||
*/
|
||||
bool operator==(const ListPolyIt& other) const
|
||||
{
|
||||
return &poly == &other.poly && it == other.it;
|
||||
}
|
||||
void operator=(const ListPolyIt& other) { poly = other.poly; it = other.it; }
|
||||
//!< move the iterator forward (and wrap around at the end)
|
||||
ListPolyIt& operator++()
|
||||
|
||||
@@ -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;
|
||||
// Slice layer z layer last current fan speed and layer retraction comb travel travel avoid
|
||||
// storage nr height position extruder time settings combing offset avoid distance
|
||||
gCodePlanner = new GCodePlanner(*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, SpaceFillType::Lines, 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
|
||||
|
||||
+245
-70
@@ -19,77 +19,252 @@ void LinearAlg2DTest::tearDown()
|
||||
//Do nothing.
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentTest()
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalNearTest()
|
||||
{
|
||||
char is_beyond;
|
||||
|
||||
//Horizontal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(25,3),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(25,0),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
//Points beyond the horizontal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(200,0),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-100,0),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(2), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-1,-1),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,3),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
|
||||
//Vertical line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(25), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(5,25),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,25),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
//Points beyond the vertical line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,200),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,-100),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(2), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-1,-1),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(3,0),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
|
||||
//45 degrees diagonal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(50), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(30,20),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(25,25),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
//Points beyond the diagonal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(20000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(200,200),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(20000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-100,-100),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-3,0),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(18), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(3,-3),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
|
||||
//Arbitrary degree diagonal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(320), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(20,30),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(40,20),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,0),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
//Points beyond the diagonal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(12500),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(200,100),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(12500),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-100,-50),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-3,0),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(20), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-2,4),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
|
||||
//Zero-distance line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(100), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,0),Point(10,0),&is_beyond));
|
||||
CPPUNIT_ASSERT(is_beyond != 0); //It's beyond, but which direction it is beyond in doesn't matter.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,0),Point(0,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LinearAlg2DTest::getAngleStraightTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(100, 1), 1.0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngle45CcwTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, -100), 1.75);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngle90CcwTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(0, -100), 1.5);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngle90CwTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(0, 100), .5);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleStraightBackTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, 1), 0.0);
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, -1), 2.0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleLeftAABTest()
|
||||
{
|
||||
LinearAlg2D::getAngleLeft(Point(0, 0), Point(0, 0), Point(100, 0)); //Any output is allowed. Just don't crash!
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleLeftABBTest()
|
||||
{
|
||||
LinearAlg2D::getAngleLeft(Point(0, 0), Point(100, 0), Point(100, 100)); //Any output is allowed. Just don't crash!
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleLeftAAATest()
|
||||
{
|
||||
LinearAlg2D::getAngleLeft(Point(0, 0), Point(0, 0), Point(0, 0)); //Any output is allowed. Just don't crash!
|
||||
}
|
||||
|
||||
|
||||
void LinearAlg2DTest::getAngleAssert(Point a, Point b, Point c, float actual_angle_in_half_rounds)
|
||||
{
|
||||
float actual_angle = actual_angle_in_half_rounds * M_PI;
|
||||
float supposed_angle = LinearAlg2D::getAngleLeft(a, b, c);
|
||||
std::stringstream ss;
|
||||
ss << "Corner in " << a << "-" << b << "-" << c << " was computed to have an angle of " << supposed_angle << " instead of " << actual_angle << ".";
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(), std::fabs(actual_angle - supposed_angle) <= maximum_error_angle);
|
||||
}
|
||||
|
||||
}
|
||||
+113
-7
@@ -1,11 +1,12 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//UltiScanTastic is released under the terms of the AGPLv3 or higher.
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#ifndef LINEARALG2DTEST_H
|
||||
#define LINEARALG2DTEST_H
|
||||
#define LINEARALG2DTEST_H
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <../src/utils/intpoint.h>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -13,7 +14,43 @@ namespace cura
|
||||
class LinearAlg2DTest : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(LinearAlg2DTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentTest);
|
||||
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(getAngleStraightTest);
|
||||
CPPUNIT_TEST(getAngle90CcwTest);
|
||||
CPPUNIT_TEST(getAngle90CwTest);
|
||||
CPPUNIT_TEST(getAngle45CcwTest);
|
||||
CPPUNIT_TEST(getAngleStraightBackTest);
|
||||
CPPUNIT_TEST(getAngleLeftAABTest);
|
||||
CPPUNIT_TEST(getAngleLeftABBTest);
|
||||
CPPUNIT_TEST(getAngleLeftAAATest);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@@ -33,11 +70,80 @@ public:
|
||||
*/
|
||||
void tearDown();
|
||||
|
||||
/*!
|
||||
* \brief Tests the LinearAlg2D#getDist2FromLineSegment function.
|
||||
*/
|
||||
void getDist2FromLineSegmentTest();
|
||||
//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();
|
||||
|
||||
void getAngleStraightTest();
|
||||
void getAngle90CcwTest();
|
||||
void getAngle90CwTest();
|
||||
void getAngle45CcwTest();
|
||||
void getAngleStraightBackTest();
|
||||
void getAngleLeftAABTest();
|
||||
void getAngleLeftABBTest();
|
||||
void getAngleLeftAAATest();
|
||||
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief The maximum allowed error in angle measurements.
|
||||
*/
|
||||
static constexpr float maximum_error_angle = 1.0;
|
||||
|
||||
/*!
|
||||
* Performs the assertion of the getAngle tests
|
||||
*
|
||||
* \param a the a parameter of getAngle
|
||||
* \param b the b parameter of getAngle
|
||||
* \param c the c parameter of getAngle
|
||||
* \param actual_angle_in_half_rounds the actual angle where 0.5 equals ???
|
||||
*/
|
||||
void getAngleAssert(Point a, Point b, Point c, float actual_angle_in_half_rounds);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
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.
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário